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("", "", |_| {}, |_| {}); }