diff --git a/CHANGELOG.md b/CHANGELOG.md
index da1416c..051bbf3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,23 +10,57 @@ Subheadings to categorize changes are `added, changed, deprecated, removed, fixe
## Unreleased
-## 0.2.0
+This release has an [MSRV][] of 1.75.
-### added
+## [0.3.0] - 2024/08/15
+
+This release has an [MSRV][] of 1.75.
+
+### Added
+
+- `set_hook_with` now takes an additional argument, `on_panic_callback` which is triggered automatically on panic. ([#3] by [@simbleau])
+
+### Fixed
+
+- If a panic occurs after the first panic (which can happen in multi-threaded apps, e.g. Bevy games), it will only be logged to stdout. Hence, only the first panic that occurred is preserved. ([#3] by [@simbleau])
+
+## [0.2.0] - 2024/04/15
+
+This release has an [MSRV][] of 1.75.
+
+### Added
- `FORM_TEXTAREA_ID` is now public, which has the element ID of the stack trace text area.
- `FORM_SUBMIT_ID` is now public, which has the element ID of the submit button.
-### changed
+### Changed
+
+- `set_hook_with` now takes the additional argument of an HTML form structure. The previous, default form can still be used with `web_panic_report::set_default_hook_with`, however requires the new (default) cargo feature: `default-form`. ([#2] by [@simbleau])
-- `set_hook_with` now takes the additional argument of an HTML form structure. The previous, default form can still be used with `web_panic_report::set_default_hook_with`, however requires the new (default) cargo feature: `default-form`.
+## [0.1.1] - 2024/03/17
-## 0.1.1
+This release has an [MSRV][] of 1.75.
-### changed
+### Fixed
- Any patch version of `web-sys` or `wasm-bindgen` can now be used.
-## 0.1.0
+## [0.1.0] - 2024/03/17
+
+This release has an [MSRV][] of 1.75.
+
+- Initialize release ([@Vrixyz] and [@simbleau])
+
+[@simbleau]: https://github.com/simbleau
+[@Vrixyz]: https://github.com/Vrixyz
+
+[#2]: https://github.com/loopystudios/web_panic_report/pull/2
+[#3]: https://github.com/loopystudios/web_panic_report/pull/3
+
+[Unreleased]: https://github.com/loopystudios/web_panic_report/compare/v0.3.0...HEAD
+[0.3.0]: https://github.com/loopystudios/web_panic_report/compare/v0.2.0...v0.3.0
+[0.2.0]: https://github.com/loopystudios/web_panic_report/compare/v0.1.1...v0.2.0
+[0.1.1]: https://github.com/loopystudios/web_panic_report/compare/v0.1.0...v0.1.1
+[0.1.0]: https://github.com/loopystudios/web_panic_report/releases/tag/v0.1.0
-- Initialize release
+[MSRV]: README.md#minimum-supported-rust-version-msrv
diff --git a/Readme.md b/Readme.md
index b68d7ae..fbd8fe5 100644
--- a/Readme.md
+++ b/Readme.md
@@ -72,6 +72,26 @@ You can also use a custom bug report form. See the [custom example](examples/cus
- [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) - Only outputs stack trace to the console.
+## Minimum supported Rust Version (MSRV)
+
+This version of Web Panic Report has been verified to compile with **Rust 1.75** and later.
+
+Future versions of Web Panic Report might increase the Rust version requirement.
+It will not be treated as a breaking change and as such can even happen with small patch releases.
+
+
+Click here if compiling fails.
+
+As time has passed, some of Web Panic Report's dependencies could have released versions with a higher Rust requirement.
+If you encounter a compilation issue due to a dependency and don't want to upgrade your Rust toolchain, then you could downgrade the dependency.
+
+```sh
+# Use the problematic dependency's name and version
+cargo update -p package_name --precise 0.1.1
+```
+
+
+
## Community
All Loopy projects and development happens in the [Loopy Discord](https://discord.gg/zrjnQzdjCB). The discord is open to the public.
diff --git a/examples/custom.rs b/examples/custom.rs
index 675a0a6..4863801 100644
--- a/examples/custom.rs
+++ b/examples/custom.rs
@@ -19,10 +19,17 @@ fn main() {
);
// Set the panic hook at the beginning of your program
- web_panic_report::set_hook_with("test-container", my_form, |panic_info| {
- web_sys::window()
- .unwrap()
- .alert_with_message(&panic_info.display.to_string())
- .unwrap();
- });
+ web_panic_report::set_hook_with(
+ "test-container",
+ my_form,
+ // No-op on panic
+ |_| {},
+ // Submit button will cause a web browser alert
+ |panic_info| {
+ web_sys::window()
+ .unwrap()
+ .alert_with_message(&panic_info.display.to_string())
+ .unwrap();
+ },
+ );
}
diff --git a/rustfmt.toml b/rustfmt.toml
index 988685b..d1c9a17 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1,6 +1,7 @@
max_width = 100
-# TODO: comment_width = 100 - Wait for this to be stable
-# TODO: wrap_comments = true - Wait for this to be stable
use_field_init_shorthand = true
newline_style = "Unix"
-# TODO: imports_granularity = "Crate" - Wait for this to be stable.
+# TODO: Enable - Currently nightly only :(
+# wrap_comments = true
+# comment_width = 100
+# imports_granularity = "Crate"
diff --git a/src/default_form/mod.rs b/src/default_form/mod.rs
index 90862a0..c2be391 100644
--- a/src/default_form/mod.rs
+++ b/src/default_form/mod.rs
@@ -15,5 +15,5 @@ pub fn set_default_hook_with(container_id: impl Into, submit_callback
where
F: Fn(&WasmPanicInfo) + Send + Sync + 'static,
{
- crate::set_hook_with(container_id, FORM_HTML, submit_callback)
+ crate::set_hook_with(container_id, FORM_HTML, |_| {}, submit_callback)
}
diff --git a/src/lib.rs b/src/lib.rs
index 904b912..41d6156 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,7 +1,9 @@
-use std::sync::Arc;
+use std::sync::{
+ atomic::{AtomicBool, Ordering},
+ Arc,
+};
use wasm_bindgen::prelude::*;
-use web_sys::wasm_bindgen::JsCast;
-use web_sys::{Element, HtmlButtonElement, HtmlTextAreaElement};
+use web_sys::{wasm_bindgen::JsCast, Element, HtmlButtonElement, HtmlTextAreaElement};
#[cfg(feature = "default-form")]
mod default_form;
@@ -30,7 +32,8 @@ pub const FORM_SUBMIT_ID: &str = "panic_info_form_submit";
/// Information about the panic that occurred, potentially useful to report.
///
-/// Why not [`PanicInfo`](std::panic::PanicInfo)? `PanicInfo` is unable to be owned, doesn't implement `Clone`, and is `!Send`, making it unable to pass to a callback.
+/// Why not [`PanicInfo`](std::panic::PanicInfo)? `PanicInfo` is unable to be owned, doesn't
+/// implement `Clone`, and is `!Send`, making it unable to pass to a callback.
#[derive(Debug)]
pub struct WasmPanicInfo {
/// The file that triggered the panic
@@ -55,20 +58,25 @@ impl std::fmt::Display for WasmPanicInfo {
/// # Params
/// `container_id`: The ID of the HTML element that will be unmounted in favor of the form.\
/// `form_html`: The raw HTML that will replace the container.\
+/// `on_panic_callback`: A closure that is triggered on panic automatically.\
/// `submit_callback`: The closure that will run when the user hits the send report button.
///
/// # Panics
-/// This will panic (ironically) if the panic occurs in a headless environment.
-pub fn set_hook_with(
+/// This will panic (ironically) if the panic is caught in a headless environment.
+pub fn set_hook_with(
container_id: impl Into,
form_html: impl Into,
- submit_callback: F,
+ on_panic_callback: F1,
+ submit_callback: F2,
) where
- F: Fn(&WasmPanicInfo) + Send + Sync + 'static,
+ F1: Fn(&WasmPanicInfo) + Send + Sync + 'static,
+ F2: Fn(&WasmPanicInfo) + Send + Sync + 'static,
{
let form_html = form_html.into();
let container_id = container_id.into();
- let callback = Arc::new(submit_callback);
+ let already_hydrated: AtomicBool = AtomicBool::new(false);
+ let on_panic_callback = Arc::new(on_panic_callback);
+ let submit_callback = Arc::new(submit_callback);
std::panic::set_hook(Box::new(move |panic_info| {
// Collect stack trace
@@ -83,8 +91,34 @@ pub fn set_hook_with(
stack_trace.push_str("\n\n");
stack_trace
};
+ // Print error
error(console_message.clone());
+ if already_hydrated.swap(true, Ordering::Relaxed) {
+ // Error already hydrated to the form body
+ return;
+ }
+
+ let wasm_panic_info = WasmPanicInfo {
+ file: panic_info
+ .location()
+ .map(|pi: &std::panic::Location<'_>| pi.file())
+ .unwrap_or_default()
+ .to_owned(),
+ line: panic_info
+ .location()
+ .map(|pi| pi.line())
+ .unwrap_or_default(),
+ col: panic_info
+ .location()
+ .map(|pi| pi.column())
+ .unwrap_or_default(),
+ display: panic_info.to_string(),
+ stack: e.stack(),
+ };
+ // Trigger callback on panic.
+ on_panic_callback(&wasm_panic_info);
+
// Retrieve the parent container we will be replacing with the report form.
let window = web_sys::window().expect("global window does not exists");
let document = window.document().expect("expecting a document on window");
@@ -93,40 +127,23 @@ pub fn set_hook_with(
.unwrap_or_else(|| {
panic!("`web_panic_report`: element `{container_id}` does not exist in the DOM",)
});
-
// Replace inner html with our report form
parent.set_inner_html(&form_html);
- // Replace the stack trace
+
+ // Hydrate the stack trace
let text_area: HtmlTextAreaElement = document
.get_element_by_id(FORM_TEXTAREA_ID)
.expect("form text id doesn't exist")
.unchecked_into();
text_area.set_value(&console_message);
- // TODO: Add onclick handler to button
+
+ // Add onclick handler to the submit button
let submit_button: HtmlButtonElement = document
.get_element_by_id(FORM_SUBMIT_ID)
.expect("form submit id doesn't exist")
.unchecked_into();
- // Add onclick handler to the button
- let wasm_panic_info = WasmPanicInfo {
- file: panic_info
- .location()
- .map(|pi: &std::panic::Location<'_>| pi.file())
- .unwrap_or_default()
- .to_owned(),
- line: panic_info
- .location()
- .map(|pi| pi.line())
- .unwrap_or_default(),
- col: panic_info
- .location()
- .map(|pi| pi.column())
- .unwrap_or_default(),
- display: panic_info.to_string(),
- stack: e.stack(),
- };
let closure: Closure = Closure::new({
- let callback = callback.clone();
+ let callback = submit_callback.clone();
move || {
callback(&wasm_panic_info);
}
diff --git a/tests/tests.rs b/tests/tests.rs
index 25abbf9..3701d05 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -1,4 +1,4 @@
#[test]
fn can_set_as_hook() {
- web_panic_report::set_hook_with("", "", |_| {});
+ web_panic_report::set_hook_with("", "", |_| {}, |_| {});
}