wry_bindgen/
cast.rs

1//! JsCast - Type casting trait for JavaScript types
2//!
3//! This trait provides methods for casting between JavaScript types,
4//! similar to wasm-bindgen's JsCast trait.
5
6use crate::JsValue;
7
8/// Trait for types that can be cast to and from JsValue.
9///
10/// This is the wry-bindgen equivalent of wasm-bindgen's `JsCast` trait.
11/// It enables safe and unsafe casting between JavaScript types.
12pub trait JsCast: AsRef<JsValue> + Into<JsValue> + Sized {
13    /// Check if a JsValue is an instance of this type.
14    ///
15    /// This performs a runtime instanceof check in JavaScript.
16    fn instanceof(val: &JsValue) -> bool;
17
18    /// Performs a dynamic type check to see whether the `JsValue` provided
19    /// is a value of this type.
20    ///
21    /// Unlike `instanceof`, this can be specialized to check for primitive types
22    /// or perform other type checks that aren't possible with instanceof.
23    /// The default implementation falls back to `instanceof`.
24    #[inline]
25    fn is_type_of(val: &JsValue) -> bool {
26        Self::instanceof(val)
27    }
28
29    /// Test whether this JS value has a type `T`.
30    ///
31    /// This method will dynamically check to see if this JS object can be
32    /// casted to the JS object of type `T`. Usually this uses the `instanceof`
33    /// operator, but can be customized with `is_type_of`. This also works
34    /// with primitive types like booleans/strings/numbers as well as cross-realm
35    /// objects like `Array` which can originate from other iframes.
36    ///
37    /// In general this is intended to be a more robust version of
38    /// `is_instance_of`, but if you want strictly the `instanceof` operator
39    /// it's recommended to use that instead.
40    #[inline]
41    fn has_type<T: JsCast>(&self) -> bool {
42        T::is_type_of(self.as_ref())
43    }
44
45    /// Unchecked cast from JsValue to this type.
46    ///
47    /// # Safety
48    /// This does not perform any runtime checks. The caller must ensure
49    /// the value is actually of the correct type.
50    fn unchecked_from_js(val: JsValue) -> Self;
51
52    /// Unchecked cast from a JsValue reference to a reference of this type.
53    ///
54    /// # Safety
55    /// This does not perform any runtime checks. The caller must ensure
56    /// the value is actually of the correct type.
57    fn unchecked_from_js_ref(val: &JsValue) -> &Self;
58
59    /// Try to cast this value to type T.
60    ///
61    /// Returns `Ok(T)` if the value is an instance of T,
62    /// otherwise returns `Err(self)` with the original value.
63    fn dyn_into<T: JsCast>(self) -> Result<T, Self> {
64        if T::is_type_of(self.as_ref()) {
65            Ok(T::unchecked_from_js(self.into()))
66        } else {
67            Err(self)
68        }
69    }
70
71    /// Try to get a reference to type T from this value.
72    ///
73    /// Returns `Some(&T)` if the value is an instance of T,
74    /// otherwise returns `None`.
75    fn dyn_ref<T: JsCast>(&self) -> Option<&T> {
76        if T::is_type_of(self.as_ref()) {
77            Some(T::unchecked_from_js_ref(self.as_ref()))
78        } else {
79            None
80        }
81    }
82
83    /// Test whether this JS value is an instance of the type `T`.
84    ///
85    /// This method performs a dynamic check (at runtime) using the JS
86    /// `instanceof` operator. This method returns `self instanceof T`.
87    ///
88    /// Note that `instanceof` does not always work with primitive values or
89    /// across different realms (e.g. iframes). If you're not sure whether you
90    /// specifically need only `instanceof` it's recommended to use `has_type`
91    /// instead.
92    fn is_instance_of<T: JsCast>(&self) -> bool {
93        T::instanceof(self.as_ref())
94    }
95
96    /// Unchecked cast to another type.
97    fn unchecked_into<T: JsCast>(self) -> T {
98        T::unchecked_from_js(self.into())
99    }
100
101    /// Unchecked cast to a reference of another type.
102    fn unchecked_ref<T: JsCast>(&self) -> &T {
103        T::unchecked_from_js_ref(self.as_ref())
104    }
105}
106
107/// Implement JsCast for JsValue itself (identity cast)
108impl JsCast for JsValue {
109    fn instanceof(_val: &JsValue) -> bool {
110        true // Everything is a JsValue
111    }
112
113    fn unchecked_from_js(val: JsValue) -> Self {
114        val
115    }
116
117    fn unchecked_from_js_ref(val: &JsValue) -> &Self {
118        val
119    }
120}
121
122impl AsRef<JsValue> for JsValue {
123    fn as_ref(&self) -> &JsValue {
124        self
125    }
126}