diff --git a/src/lib.rs b/src/lib.rs index 7a94632dcbf..8335449521a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,10 +36,11 @@ macro_rules! if_std { /// use wasm_bindgen::prelude::*; /// ``` pub mod prelude { - pub use wasm_bindgen_macro::wasm_bindgen; #[doc(hidden)] pub use wasm_bindgen_macro::__wasm_bindgen_class_marker; + pub use wasm_bindgen_macro::wasm_bindgen; pub use JsValue; + pub use UnwrapThrowExt; if_std! { pub use closure::Closure; @@ -640,6 +641,75 @@ pub fn throw_val(s: JsValue) -> ! { } } +/// An extension trait for `Option` and `Result` for unwraping the `T` +/// value, or throwing a JS error if it is not available. +/// +/// These methods should have a smaller code size footprint than the normal +/// `Option::unwrap` and `Option::expect` methods, but they are specific to +/// working with wasm and JS. +/// +/// On non-wasm32 targets, defaults to the normal unwrap/expect calls. +/// +/// # Example +/// +/// ```no_run +/// // If the value is `Option::Some` or `Result::Ok`, then we just get the +/// // contained `T` value. +/// let x = Some(42); +/// assert_eq!(x.unwrap_throw(), 42); +/// +/// let y: Option = None; +/// +/// // This call would throw an error to JS! +/// // +/// // y.unwrap_throw() +/// // +/// // And this call would throw an error to JS with a custom error message! +/// // +/// // y.expect_throw("woopsie daisy!") +/// ``` +pub trait UnwrapThrowExt: Sized { + /// Unwrap this `Option` or `Result`, but instead of panicking on failure, + /// throw an exception to JavaScript. + fn unwrap_throw(self) -> T { + self.expect_throw("`unwrap_throw` failed") + } + + /// Unwrap this container's `T` value, or throw an error to JS with the + /// given message if the `T` value is unavailable (e.g. an `Option` is + /// `None`). + fn expect_throw(self, message: &str) -> T; +} + +impl UnwrapThrowExt for Option { + fn expect_throw(self, message: &str) -> T { + if cfg!(all(target_arch = "wasm32", not(target_os = "emscripten"))) { + match self { + Some(val) => val, + None => throw_str(message), + } + } else { + self.expect(message) + } + } +} + +impl UnwrapThrowExt for Result +where + E: core::fmt::Debug, +{ + fn expect_throw(self, message: &str) -> T { + if cfg!(all(target_arch = "wasm32", not(target_os = "emscripten"))) { + match self { + Ok(val) => val, + Err(_) => throw_str(message), + } + } else { + self.expect(message) + } + } +} + /// Returns a handle to this wasm instance's `WebAssembly.Module` /// /// Note that this is only available when the final wasm app is built with diff --git a/tests/unwrap_throw.rs b/tests/unwrap_throw.rs new file mode 100644 index 00000000000..8a45004ed87 --- /dev/null +++ b/tests/unwrap_throw.rs @@ -0,0 +1,23 @@ +extern crate wasm_bindgen; +use wasm_bindgen::prelude::*; + +#[test] +fn unwrap_throw_ok() { + assert_eq!(Some(42).unwrap_throw(), 42); + let x: Result = Ok(42); + assert_eq!(x.unwrap_throw(), 42); +} + +#[test] +#[should_panic] +fn unwrap_throw_none() { + let x: Option = None; + x.unwrap_throw(); +} + +#[test] +#[should_panic] +fn unwrap_throw_err() { + let x: Result = Err(()); + x.unwrap_throw(); +}