use std::fmt; use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; #[derive(Clone, Debug)] pub enum MyError { Variant, InflightShouldBeFalse, } // shouldn't technically need this, surely impl std::error::Error for MyError {} impl fmt::Display for MyError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "MyError::{:?}", self) } } #[wasm_bindgen(module = "tests/wasm/result.js")] extern "C" { fn error_new(message: &str) -> JsValue; } impl From for JsValue { fn from(e: MyError) -> Self { error_new(&format!("{}", e)) } } macro_rules! call_test { ($test_fn:ident, $js_fn:ident) => { #[wasm_bindgen_test] fn $test_fn() { #[wasm_bindgen(module = "tests/wasm/result.js")] extern "C" { fn $js_fn(); } $js_fn(); } }; } #[wasm_bindgen] pub fn return_my_err() -> Result { let e = Err(MyError::Variant)?; Ok(e) } call_test!(test_err, call_err); #[wasm_bindgen] pub fn return_my_ok() -> Result { Ok(5) } call_test!(test_ok, call_ok); #[wasm_bindgen] pub struct Struct { inflight: bool, } call_test!(test_struct, all_struct_methods); struct ResetOnDrop<'a> { flag: &'a mut bool, } impl<'a> Drop for ResetOnDrop<'a> { fn drop(&mut self) { *self.flag = false; } } impl<'a> ResetOnDrop<'a> { fn new(flag: &'a mut bool) -> Result { if *flag { return Err(MyError::InflightShouldBeFalse); } Ok(Self { flag }) } } #[wasm_bindgen] impl Struct { #[wasm_bindgen] pub fn new() -> Result { Ok(Struct { inflight: false }) } #[wasm_bindgen] pub fn new_err() -> Result { Err(MyError::Variant) } #[wasm_bindgen] pub fn return_ok(&mut self) -> Result { let _guard = ResetOnDrop::new(&mut self.inflight)?; Ok(5) } #[wasm_bindgen] pub fn return_err(&mut self) -> Result { let guard = ResetOnDrop::new(&mut self.inflight)?; let err = Err(MyError::Variant); let nope = err?; // we are checking both for the flag being reset (from js, via is_inflight) // and for the running of drop code drop(guard); Ok(nope) } #[wasm_bindgen] pub fn is_inflight(&self) -> bool { self.inflight } } // check some more Ok types #[wasm_bindgen] pub fn return_string() -> Result { Ok("string here".into()) } call_test!(test_return_string, call_return_string); // now we check that jsvalue works, as it did before #[wasm_bindgen] pub fn return_jsvalue_ok() -> Result { Ok(5) } call_test!(test_jsvalue_ok, call_jsvalue_ok); #[wasm_bindgen] pub fn return_jsvalue_err() -> Result { Err(JsValue::from(-1i32)) } call_test!(test_jsvalue_err, call_jsvalue_err); // test strings (they have a deferred free, in a finally block: tricky) #[wasm_bindgen] pub fn return_string_ok() -> Result { Ok("Ok".into()) } call_test!(test_string_ok, call_string_ok); #[wasm_bindgen] pub fn return_string_err() -> Result { Err("Er".into()) } call_test!(test_string_err, call_string_err); // test enums #[wasm_bindgen] pub enum MyEnum { One = 1, Two = 2, } #[wasm_bindgen] pub fn return_enum_ok() -> Result { Ok(MyEnum::Two) } call_test!(test_enum_ok, call_enum_ok); #[wasm_bindgen] pub fn return_enum_err() -> Result { Err(MyError::Variant) } call_test!(test_enum_err, call_enum_err); // T = Unit #[wasm_bindgen] pub fn return_unit_ok() -> Result<(), MyError> { Ok(()) } #[wasm_bindgen] pub fn return_unit_err() -> Result<(), MyError> { Err(MyError::Variant) } call_test!(test_unit, call_unit); // T = Option #[wasm_bindgen] pub fn return_option_ok_some() -> Result, MyError> { Ok(Some(10f64)) } #[wasm_bindgen] pub fn return_option_ok_none() -> Result, MyError> { Ok(None) } #[wasm_bindgen] pub fn return_option_err() -> Result, MyError> { Err(MyError::Variant) } call_test!(test_option, call_option);