1 | use crate::{describe::WasmDescribe, JsValue}; |
2 | |
3 | /// A trait for checked and unchecked casting between JS types. |
4 | /// |
5 | /// Specified [in an RFC][rfc] this trait is intended to provide support for |
6 | /// casting JS values between different types of one another. In JS there aren't |
7 | /// many static types but we've ascribed JS values with static types in Rust, |
8 | /// yet they often need to be switched to other types temporarily! This trait |
9 | /// provides both checked and unchecked casting into various kinds of values. |
10 | /// |
11 | /// This trait is automatically implemented for any type imported in a |
12 | /// `#[wasm_bindgen]` `extern` block. |
13 | /// |
14 | /// [rfc]: https://github.com/rustwasm/rfcs/blob/master/text/002-wasm-bindgen-inheritance-casting.md |
15 | pub trait JsCast |
16 | where |
17 | Self: AsRef<JsValue> + Into<JsValue>, |
18 | { |
19 | /// Test whether this JS value has a type `T`. |
20 | /// |
21 | /// This method will dynamically check to see if this JS object can be |
22 | /// casted to the JS object of type `T`. Usually this uses the `instanceof` |
23 | /// operator. This also works with primitive types like |
24 | /// booleans/strings/numbers as well as cross-realm object like `Array` |
25 | /// which can originate from other iframes. |
26 | /// |
27 | /// In general this is intended to be a more robust version of |
28 | /// `is_instance_of`, but if you want strictly the `instanceof` operator |
29 | /// it's recommended to use that instead. |
30 | fn has_type<T>(&self) -> bool |
31 | where |
32 | T: JsCast, |
33 | { |
34 | T::is_type_of(self.as_ref()) |
35 | } |
36 | |
37 | /// Performs a dynamic cast (checked at runtime) of this value into the |
38 | /// target type `T`. |
39 | /// |
40 | /// This method will return `Err(self)` if `self.has_type::<T>()` |
41 | /// returns `false`, and otherwise it will return `Ok(T)` manufactured with |
42 | /// an unchecked cast (verified correct via the `has_type` operation). |
43 | fn dyn_into<T>(self) -> Result<T, Self> |
44 | where |
45 | T: JsCast, |
46 | { |
47 | if self.has_type::<T>() { |
48 | Ok(self.unchecked_into()) |
49 | } else { |
50 | Err(self) |
51 | } |
52 | } |
53 | |
54 | /// Performs a dynamic cast (checked at runtime) of this value into the |
55 | /// target type `T`. |
56 | /// |
57 | /// This method will return `None` if `self.has_type::<T>()` |
58 | /// returns `false`, and otherwise it will return `Some(&T)` manufactured |
59 | /// with an unchecked cast (verified correct via the `has_type` operation). |
60 | fn dyn_ref<T>(&self) -> Option<&T> |
61 | where |
62 | T: JsCast, |
63 | { |
64 | if self.has_type::<T>() { |
65 | Some(self.unchecked_ref()) |
66 | } else { |
67 | None |
68 | } |
69 | } |
70 | |
71 | /// Performs a zero-cost unchecked cast into the specified type. |
72 | /// |
73 | /// This method will convert the `self` value to the type `T`, where both |
74 | /// `self` and `T` are simple wrappers around `JsValue`. This method **does |
75 | /// not check whether `self` is an instance of `T`**. If used incorrectly |
76 | /// then this method may cause runtime exceptions in both Rust and JS, this |
77 | /// should be used with caution. |
78 | fn unchecked_into<T>(self) -> T |
79 | where |
80 | T: JsCast, |
81 | { |
82 | T::unchecked_from_js(self.into()) |
83 | } |
84 | |
85 | /// Performs a zero-cost unchecked cast into a reference to the specified |
86 | /// type. |
87 | /// |
88 | /// This method will convert the `self` value to the type `T`, where both |
89 | /// `self` and `T` are simple wrappers around `JsValue`. This method **does |
90 | /// not check whether `self` is an instance of `T`**. If used incorrectly |
91 | /// then this method may cause runtime exceptions in both Rust and JS, this |
92 | /// should be used with caution. |
93 | /// |
94 | /// This method, unlike `unchecked_into`, does not consume ownership of |
95 | /// `self` and instead works over a shared reference. |
96 | fn unchecked_ref<T>(&self) -> &T |
97 | where |
98 | T: JsCast, |
99 | { |
100 | T::unchecked_from_js_ref(self.as_ref()) |
101 | } |
102 | |
103 | /// Test whether this JS value is an instance of the type `T`. |
104 | /// |
105 | /// This method performs a dynamic check (at runtime) using the JS |
106 | /// `instanceof` operator. This method returns `self instanceof T`. |
107 | /// |
108 | /// Note that `instanceof` does not always work with primitive values or |
109 | /// across different realms (e.g. iframes). If you're not sure whether you |
110 | /// specifically need only `instanceof` it's recommended to use `has_type` |
111 | /// instead. |
112 | fn is_instance_of<T>(&self) -> bool |
113 | where |
114 | T: JsCast, |
115 | { |
116 | T::instanceof(self.as_ref()) |
117 | } |
118 | |
119 | /// Performs a dynamic `instanceof` check to see whether the `JsValue` |
120 | /// provided is an instance of this type. |
121 | /// |
122 | /// This is intended to be an internal implementation detail, you likely |
123 | /// won't need to call this. It's generally called through the |
124 | /// `is_instance_of` method instead. |
125 | fn instanceof(val: &JsValue) -> bool; |
126 | |
127 | /// Performs a dynamic check to see whether the `JsValue` provided |
128 | /// is a value of this type. |
129 | /// |
130 | /// Unlike `instanceof`, this can be specialised to use a custom check by |
131 | /// adding a `#[wasm_bindgen(is_type_of = callback)]` attribute to the |
132 | /// type import declaration. |
133 | /// |
134 | /// Other than that, this is intended to be an internal implementation |
135 | /// detail of `has_type` and you likely won't need to call this. |
136 | fn is_type_of(val: &JsValue) -> bool { |
137 | Self::instanceof(val) |
138 | } |
139 | |
140 | /// Performs a zero-cost unchecked conversion from a `JsValue` into an |
141 | /// instance of `Self` |
142 | /// |
143 | /// This is intended to be an internal implementation detail, you likely |
144 | /// won't need to call this. |
145 | fn unchecked_from_js(val: JsValue) -> Self; |
146 | |
147 | /// Performs a zero-cost unchecked conversion from a `&JsValue` into an |
148 | /// instance of `&Self`. |
149 | /// |
150 | /// Note the safety of this method, which basically means that `Self` must |
151 | /// be a newtype wrapper around `JsValue`. |
152 | /// |
153 | /// This is intended to be an internal implementation detail, you likely |
154 | /// won't need to call this. |
155 | fn unchecked_from_js_ref(val: &JsValue) -> &Self; |
156 | } |
157 | |
158 | /// Trait implemented for wrappers around `JsValue`s generated by `#[wasm_bindgen]`. |
159 | #[doc (hidden)] |
160 | pub trait JsObject: JsCast + WasmDescribe {} |
161 | |