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