from_js_ref 0.2.0

Trait for converting from JS interface reference wrappers
Documentation
use from_js_ref::FromJsRef as _;
use wasm_bindgen::prelude::*;
use wasm_refgen::wasm_refgen;

#[derive(Clone)]
#[wasm_bindgen(js_name = Foo)]
pub struct WasmFoo {
    _value: u32,
}

#[wasm_refgen(js_ref = JsFoo)]
#[wasm_bindgen(js_class = "Foo")]
impl WasmFoo {
    #[wasm_bindgen(constructor)]
    pub fn new(value: u32) -> Self {
        Self { _value: value }
    }
}

/// The macro generates a `JsFoo` extern type. Verify it exists and
/// implements `JsCast` (which `wasm-bindgen` derives for extern types).
#[test]
fn generated_js_ref_type_implements_js_cast() {
    fn assert_js_cast<T: JsCast>() {}
    assert_js_cast::<JsFoo>();
}

/// The macro generates `FromJsRef for WasmFoo` with `JsRef = JsFoo`.
#[test]
fn generated_from_js_ref_impl() {
    fn assert_from_js_ref<T: from_js_ref::FromJsRef>() {}
    assert_from_js_ref::<WasmFoo>();
}

/// `From<&JsFoo> for WasmFoo` is generated by the macro.
#[test]
fn generated_from_js_ref_for_type() {
    fn assert_from<T, U: From<T>>() {}
    assert_from::<&JsFoo, WasmFoo>();
}

/// `From<WasmFoo> for JsFoo` is generated by the macro.
#[test]
fn generated_from_type_for_js_ref() {
    fn assert_from<T, U: From<T>>() {}
    assert_from::<WasmFoo, JsFoo>();
}

/// `JsDeref<WasmFoo>` is blanket-implemented for `JsFoo` via `FromJsRef`.
#[test]
fn generated_js_deref_impl() {
    fn assert_js_deref<T: from_js_ref::JsDeref<WasmFoo>>() {}
    assert_js_deref::<JsFoo>();
}

/// The generated `JsFoo` extern type satisfies `JsCast + AsRef<JsValue>`,
/// confirming it can be used in `dyn_ref`/`dyn_into` call sites at the
/// type level. Note: `instanceof` checks at runtime use `JsFoo` (the Rust
/// ident) as the JS class name, which won't match the exported struct's
/// class. Use `FromJsRef::try_from_js_value` instead of `dyn_into`.
#[test]
fn js_ref_type_is_js_cast_compatible() {
    fn assert_castable<T: JsCast + AsRef<JsValue>>() {}
    assert_castable::<JsFoo>();
}

/// The generated `FromJsRef` impl overrides `try_from_js_value` to use a
/// duck-type check (`Reflect::has`) instead of the broken `instanceof`.
/// We can only verify the method exists at compile time on native targets —
/// `Reflect::has` panics outside Wasm.
#[test]
fn try_from_js_value_is_defined() {
    // Compile-time proof that try_from_js_value exists with the right signature.
    fn assert_try_from<T: from_js_ref::FromJsRef>(_f: fn(&JsValue) -> Option<T>) {}
    assert_try_from::<WasmFoo>(WasmFoo::try_from_js_value);
}

// Repeat with a different naming convention to ensure the macro handles
// multi-word class names correctly.

#[derive(Clone)]
#[wasm_bindgen(js_name = CommitWithBlob)]
pub struct WasmCommitWithBlob {
    _data: u32,
}

#[wasm_refgen(js_ref = JsCommitWithBlob)]
#[wasm_bindgen(js_class = "CommitWithBlob")]
impl WasmCommitWithBlob {
    #[wasm_bindgen(constructor)]
    pub fn new(data: u32) -> Self {
        Self { _data: data }
    }
}

#[test]
fn multi_word_class_name_generates_correct_types() {
    fn assert_js_cast<T: JsCast>() {}
    fn assert_from_js_ref<T: from_js_ref::FromJsRef>() {}

    assert_js_cast::<JsCommitWithBlob>();
    assert_from_js_ref::<WasmCommitWithBlob>();
}

#[test]
fn multi_word_class_is_js_cast_compatible() {
    fn assert_castable<T: JsCast + AsRef<JsValue>>() {}
    assert_castable::<JsCommitWithBlob>();
}

#[test]
fn multi_word_try_from_js_value_is_defined() {
    fn assert_try_from<T: from_js_ref::FromJsRef>(_f: fn(&JsValue) -> Option<T>) {}
    assert_try_from::<WasmCommitWithBlob>(WasmCommitWithBlob::try_from_js_value);
}