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}