1use core::{fmt, marker::PhantomData, ops::Deref};
4
5use crate::{
6 __rt::marker::ErasableGeneric, IntoJsGeneric, JsCast, JsError, JsGeneric, JsValue,
7 convert::UpcastFrom,
8};
9
10pub trait Promising {
12 type Resolution;
13}
14
15macro_rules! js_primitive {
16 ($name:ident, $const_name:ident, $value:expr, $display:literal, $check:expr) => {
17 #[derive(Clone, PartialEq)]
18 #[repr(transparent)]
19 pub struct $name {
20 pub obj: JsValue,
21 }
22
23 impl $name {
24 pub const $const_name: $name = Self { obj: $value };
25 }
26
27 impl Eq for $name {}
28
29 impl Default for $name {
30 fn default() -> Self {
31 Self::$const_name
32 }
33 }
34
35 impl fmt::Debug for $name {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 f.write_str($display)
38 }
39 }
40
41 impl fmt::Display for $name {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 f.write_str($display)
44 }
45 }
46
47 impl AsRef<JsValue> for $name {
48 fn as_ref(&self) -> &JsValue {
49 &self.obj
50 }
51 }
52
53 impl From<$name> for JsValue {
54 fn from(value: $name) -> JsValue {
55 value.obj
56 }
57 }
58
59 impl From<JsValue> for $name {
60 fn from(obj: JsValue) -> Self {
61 Self { obj }
62 }
63 }
64
65 impl JsCast for $name {
66 fn instanceof(value: &JsValue) -> bool {
67 $check(value)
68 }
69
70 fn is_type_of(value: &JsValue) -> bool {
71 $check(value)
72 }
73
74 fn unchecked_from_js(value: JsValue) -> Self {
75 Self { obj: value }
76 }
77
78 fn unchecked_from_js_ref(value: &JsValue) -> &Self {
79 unsafe { &*(value as *const JsValue as *const Self) }
80 }
81 }
82
83 impl_js_value_wire!(for $name, field obj);
84
85 unsafe impl crate::__rt::marker::ErasableGeneric for $name {
86 type Repr = JsValue;
87 }
88
89 impl IntoJsGeneric for $name {
90 type JsCanon = Self;
91
92 fn to_js(self) -> Self {
93 self
94 }
95 }
96
97 impl UpcastFrom<$name> for $name {}
98 impl UpcastFrom<$name> for JsValue {}
99 };
100 }
101
102js_primitive!(
103 Undefined,
104 UNDEFINED,
105 JsValue::UNDEFINED,
106 "undefined",
107 JsValue::is_undefined
108);
109js_primitive!(Null, NULL, JsValue::NULL, "null", JsValue::is_null);
110
111impl UpcastFrom<()> for Undefined {}
112impl UpcastFrom<Undefined> for () {}
113impl UpcastFrom<()> for JsValue {}
114impl UpcastFrom<()> for () {}
115
116#[derive(Clone, PartialEq)]
117#[repr(transparent)]
118pub struct JsOption<T = JsValue> {
119 pub obj: JsValue,
120 pub generics: PhantomData<fn() -> T>,
121}
122
123impl<T: JsGeneric> JsOption<T> {
124 #[inline]
125 pub fn new() -> Self {
126 Undefined::UNDEFINED.unchecked_into()
127 }
128
129 #[inline]
130 pub fn wrap(val: T) -> Self {
131 val.unchecked_into()
132 }
133
134 #[inline]
135 pub fn from_option(opt: Option<T>) -> Self {
136 match opt {
137 Some(value) => Self::wrap(value),
138 None => Self::new(),
139 }
140 }
141
142 #[inline]
143 pub fn is_empty(&self) -> bool {
144 self.obj.is_null() || self.obj.is_undefined()
145 }
146
147 #[inline]
148 pub fn as_option(&self) -> Option<T> {
149 if self.is_empty() {
150 None
151 } else {
152 Some(T::unchecked_from_js(self.obj.clone()))
153 }
154 }
155
156 #[inline]
157 pub fn into_option(self) -> Option<T> {
158 if self.is_empty() {
159 None
160 } else {
161 Some(self.unchecked_into())
162 }
163 }
164
165 #[inline]
166 pub fn unwrap(self) -> T {
167 self.expect("called `JsOption::unwrap()` on an empty value")
168 }
169
170 #[inline]
171 pub fn expect(self, msg: &str) -> T {
172 match self.into_option() {
173 Some(value) => value,
174 None => panic!("{}", msg),
175 }
176 }
177
178 #[inline]
179 pub fn unwrap_or_default(self) -> T
180 where
181 T: Default,
182 {
183 self.into_option().unwrap_or_default()
184 }
185
186 #[inline]
187 pub fn unwrap_or_else<F>(self, f: F) -> T
188 where
189 F: FnOnce() -> T,
190 {
191 self.into_option().unwrap_or_else(f)
192 }
193}
194
195impl<T: JsGeneric> Default for JsOption<T> {
196 fn default() -> Self {
197 Self::new()
198 }
199}
200
201impl<T: JsGeneric + fmt::Debug> fmt::Debug for JsOption<T> {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 write!(f, "{}?(", core::any::type_name::<T>())?;
204 match self.as_option() {
205 Some(value) => write!(f, "{value:?}")?,
206 None => f.write_str("null")?,
207 }
208 f.write_str(")")
209 }
210}
211
212impl<T: JsGeneric + fmt::Display> fmt::Display for JsOption<T> {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 write!(f, "{}?(", core::any::type_name::<T>())?;
215 match self.as_option() {
216 Some(value) => write!(f, "{value}")?,
217 None => f.write_str("null")?,
218 }
219 f.write_str(")")
220 }
221}
222
223impl<T> AsRef<JsValue> for JsOption<T> {
224 fn as_ref(&self) -> &JsValue {
225 &self.obj
226 }
227}
228
229impl<T: JsGeneric> Deref for JsOption<T> {
230 type Target = T;
231
232 fn deref(&self) -> &T {
233 T::unchecked_from_js_ref(&self.obj)
234 }
235}
236
237impl<T> From<JsOption<T>> for JsValue {
238 fn from(value: JsOption<T>) -> JsValue {
239 value.obj
240 }
241}
242
243impl<T> From<JsValue> for JsOption<T> {
244 fn from(obj: JsValue) -> Self {
245 Self {
246 obj,
247 generics: PhantomData,
248 }
249 }
250}
251
252impl<T: JsGeneric> JsCast for JsOption<T> {
253 fn instanceof(value: &JsValue) -> bool {
254 T::is_type_of(value) || value.is_null() || value.is_undefined()
255 }
256
257 fn unchecked_from_js(value: JsValue) -> Self {
258 value.into()
259 }
260
261 fn unchecked_from_js_ref(value: &JsValue) -> &Self {
262 unsafe { &*(value as *const JsValue as *const Self) }
263 }
264}
265
266impl_js_value_wire!(impl<T> for JsOption<T>, field obj);
267
268unsafe impl<T> crate::__rt::marker::ErasableGeneric for JsOption<T> {
269 type Repr = JsValue;
270}
271
272impl<T: JsGeneric> IntoJsGeneric for JsOption<T> {
273 type JsCanon = Self;
274
275 fn to_js(self) -> Self {
276 self
277 }
278}
279
280impl UpcastFrom<JsValue> for JsOption<JsValue> {}
281impl<T> UpcastFrom<Undefined> for JsOption<T> {}
282impl<T> UpcastFrom<Null> for JsOption<T> {}
283impl<T> UpcastFrom<()> for JsOption<T> {}
284impl<T> UpcastFrom<JsOption<T>> for JsValue {}
285impl<T, U> UpcastFrom<JsOption<U>> for JsOption<T> where T: UpcastFrom<U> {}
286
287impl Promising for JsValue {
288 type Resolution = JsValue;
289}
290
291impl Promising for () {
292 type Resolution = Undefined;
293}
294
295macro_rules! promising_self {
296 ($($ty:ty),* $(,)?) => {
297 $(
298 impl Promising for $ty {
299 type Resolution = $ty;
300 }
301 )*
302 };
303 }
304
305promising_self!(
306 bool, char, f32, f64, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, JsError
307);
308
309impl<T: Promising> Promising for Option<T> {
310 type Resolution = Option<T::Resolution>;
311}
312
313impl<T: ErasableGeneric + Promising, E: ErasableGeneric> Promising for Result<T, E> {
314 type Resolution = Result<T::Resolution, E>;
315}