Add the UnwrapThrowExt<T> trait

An extension trait for `Option<T>` and `Result<T, E>` 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.
This commit is contained in:
Nick Fitzgerald 2019-01-31 13:26:21 -08:00
parent 92072bf03c
commit 0e11e4a3bd
2 changed files with 94 additions and 1 deletions

View File

@ -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<T>` and `Result<T, E>` 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<i32> = 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<T>: 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<T>` is
/// `None`).
fn expect_throw(self, message: &str) -> T;
}
impl<T> UnwrapThrowExt<T> for Option<T> {
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<T, E> UnwrapThrowExt<T> for Result<T, E>
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

23
tests/unwrap_throw.rs Normal file
View File

@ -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<i32, ()> = Ok(42);
assert_eq!(x.unwrap_throw(), 42);
}
#[test]
#[should_panic]
fn unwrap_throw_none() {
let x: Option<i32> = None;
x.unwrap_throw();
}
#[test]
#[should_panic]
fn unwrap_throw_err() {
let x: Result<i32, ()> = Err(());
x.unwrap_throw();
}