Skip to main content

wry_bindgen/convert/
traits.rs

1use crate::encode::{
2    Anchored, BinaryDecode, BinaryEncode, BorrowScope, CallScoped, EncodeTypeDef, JsRef,
3    ThrowingResult,
4};
5use crate::ipc::EncodedData;
6use crate::{JsCast, JsValue};
7use core::mem::ManuallyDrop;
8use core::ops::Deref;
9
10/// Marker for types accepted by wasm-bindgen-shaped APIs that conceptually
11/// convert into a Wasm ABI value.
12///
13/// Wry-bindgen does not use wasm-bindgen's raw ABI transport on desktop; the
14/// generated glue uses the binary protocol instead. These traits are kept as
15/// markers for `js-sys`/`web-sys` signatures that use wasm-bindgen's unstable
16/// conversion traits as bounds.
17pub trait IntoWasmAbi: BinaryEncode + EncodeTypeDef {
18    #[inline]
19    fn into_abi(self) -> u32
20    where
21        Self: Sized + IntoAbiId,
22    {
23        self.into_abi_id()
24    }
25}
26
27/// Marker for types accepted by wasm-bindgen-shaped APIs that conceptually
28/// convert from a Wasm ABI value.
29pub trait FromWasmAbi: BinaryDecode + EncodeTypeDef {
30    /// Recreate a JS-reference-like value from a heap id.
31    ///
32    /// This is only a compatibility hook for crates that preserve `JsValue`
33    /// references through serde or similar adapters. Generated Wry bindings use
34    /// the binary protocol instead.
35    ///
36    /// # Safety
37    ///
38    /// The caller must pass an id for a live JavaScript heap value that is valid
39    /// for `Self`.
40    #[inline]
41    unsafe fn from_abi(js: u32) -> Self
42    where
43        Self: Sized + FromAbiId,
44    {
45        unsafe { Self::from_abi_id(js) }
46    }
47}
48
49/// Marker for types that may appear as `Option<T>` in wasm-bindgen-shaped APIs.
50pub trait OptionIntoWasmAbi: IntoWasmAbi {}
51
52/// Marker for types that may be received as `Option<T>` in wasm-bindgen-shaped APIs.
53pub trait OptionFromWasmAbi: FromWasmAbi {}
54
55/// Marker for values that have a wasm-bindgen ABI representation.
56pub trait WasmAbi {}
57
58/// Marker for types that can be borrowed from wasm-bindgen-shaped APIs.
59pub trait RefFromWasmAbi {
60    /// Recreate a non-dropping reference anchor from a heap id.
61    ///
62    /// # Safety
63    ///
64    /// The caller must pass an id for a live JavaScript heap value that remains
65    /// valid for the returned anchor.
66    #[inline]
67    unsafe fn ref_from_abi(js: u32) -> AbiRef<Self>
68    where
69        Self: Sized + FromAbiId,
70    {
71        AbiRef(ManuallyDrop::new(unsafe { Self::from_abi_id(js) }))
72    }
73}
74
75/// Non-dropping anchor returned by `RefFromWasmAbi::ref_from_abi`.
76pub struct AbiRef<T>(ManuallyDrop<T>);
77
78impl<T> Deref for AbiRef<T> {
79    type Target = T;
80
81    #[inline]
82    fn deref(&self) -> &Self::Target {
83        &self.0
84    }
85}
86
87impl<T> AsRef<T> for AbiRef<T> {
88    #[inline]
89    fn as_ref(&self) -> &T {
90        self
91    }
92}
93
94#[doc(hidden)]
95pub trait IntoAbiId {
96    fn into_abi_id(self) -> u32;
97}
98
99#[doc(hidden)]
100pub trait FromAbiId {
101    unsafe fn from_abi_id(js: u32) -> Self;
102}
103
104impl<T> IntoAbiId for T
105where
106    T: AsRef<JsValue>,
107{
108    #[inline]
109    fn into_abi_id(self) -> u32 {
110        let id = self.as_ref().js_ref().into_abi();
111        core::mem::forget(self);
112        id
113    }
114}
115
116impl<T> FromAbiId for T
117where
118    T: JsCast,
119{
120    #[inline]
121    unsafe fn from_abi_id(js: u32) -> Self {
122        T::unchecked_from_js(JsValue::from_ref(JsRef::from_abi(js)))
123    }
124}
125
126/// The wire type advertised to JS for a return value, in borrow scope `S` — the
127/// return-side analog of [`ArgAbi<S>::Wire`](crate::convert::ArgAbi::Wire). For
128/// [`CallScoped`] it is the value's own wire type; for [`Anchored`] it is the
129/// `Promise` *resolution* (the export macro wraps it in the configured
130/// `js_sys::Promise<…>`, since `Promise` lives in the external `js-sys` crate).
131/// The encode/lower behavior lives on the [`ReturnSync`]/[`ReturnAsync`]
132/// sub-traits, so a value implements only the scope(s) it is returnable in.
133pub trait ReturnAbi<S: BorrowScope> {
134    /// The type whose `TypeDef` is advertised to JS for this return value.
135    type Wire: EncodeTypeDef;
136}
137
138/// Encode a synchronous export's return value as wire bytes. A blanket forwards
139/// every `IntoWasmAbi` value directly; `Result` is carved out so its `Err` is
140/// thrown in JS. Because `Result` is not `IntoWasmAbi` the two do not overlap,
141/// and dispatch is by type (so it sees through type aliases).
142pub trait ReturnSync: ReturnAbi<CallScoped> {
143    /// Encode `self` as the function's return payload.
144    fn return_abi(self, encoder: &mut EncodedData);
145}
146
147impl<T: IntoWasmAbi> ReturnAbi<CallScoped> for T {
148    type Wire = T;
149}
150impl<T: IntoWasmAbi> ReturnSync for T {
151    #[inline]
152    fn return_abi(self, encoder: &mut EncodedData) {
153        self.encode(encoder);
154    }
155}
156
157impl<T, E> ReturnAbi<CallScoped> for Result<T, E>
158where
159    T: BinaryEncode + EncodeTypeDef,
160    E: Into<JsValue>,
161{
162    type Wire = ThrowingResult<T, JsValue>;
163}
164impl<T, E> ReturnSync for Result<T, E>
165where
166    T: BinaryEncode + EncodeTypeDef,
167    E: Into<JsValue>,
168{
169    #[inline]
170    fn return_abi(self, encoder: &mut EncodedData) {
171        ThrowingResult(self.map_err(Into::into)).encode(encoder);
172    }
173}
174
175// An exported constructor hands JS the stored object's handle by value (JS then
176// `__wrap`s it). `ObjectHandle` is not `IntoWasmAbi` and is only ever returned by
177// a *sync* constructor, so it implements the sync scope only.
178impl ReturnAbi<CallScoped> for crate::__rt::object_store::ObjectHandle {
179    type Wire = Self;
180}
181impl ReturnSync for crate::__rt::object_store::ObjectHandle {
182    #[inline]
183    fn return_abi(self, encoder: &mut EncodedData) {
184        self.encode(encoder);
185    }
186}
187
188/// Converts a `JsValue` into a Rust type by checking at runtime.
189pub trait TryFromJsValue: Sized {
190    fn try_from_js_value(value: JsValue) -> Result<Self, JsValue> {
191        Self::try_from_js_value_ref(&value).ok_or(value)
192    }
193
194    fn try_from_js_value_ref(value: &JsValue) -> Option<Self>;
195}
196
197/// Lowers the output of an exported `async fn` to the `Result<JsValue, JsValue>`
198/// that backs a JS promise (an `Err` becomes a rejected promise). A blanket
199/// covers every `Into<JsValue> + Promising` value; `Result` is carved out by type
200/// (it does not overlap because `Result` is not `Into<JsValue>`). The promise
201/// resolution type is [`ReturnAbi<Anchored>::Wire`], delegated to [`Promising`].
202pub trait ReturnAsync: ReturnAbi<Anchored> {
203    fn into_js_result(self) -> Result<JsValue, JsValue>;
204}
205
206impl<T> ReturnAbi<Anchored> for T
207where
208    T: Into<JsValue> + crate::sys::Promising,
209    <T as crate::sys::Promising>::Resolution: EncodeTypeDef,
210{
211    type Wire = <T as crate::sys::Promising>::Resolution;
212}
213impl<T> ReturnAsync for T
214where
215    T: Into<JsValue> + crate::sys::Promising,
216    <T as crate::sys::Promising>::Resolution: EncodeTypeDef,
217{
218    #[inline]
219    fn into_js_result(self) -> Result<JsValue, JsValue> {
220        Ok(self.into())
221    }
222}
223
224impl<T, E> ReturnAbi<Anchored> for Result<T, E>
225where
226    T: Into<JsValue> + crate::sys::Promising,
227    <T as crate::sys::Promising>::Resolution: EncodeTypeDef,
228    E: Into<JsValue>,
229{
230    type Wire = <T as crate::sys::Promising>::Resolution;
231}
232impl<T, E> ReturnAsync for Result<T, E>
233where
234    T: Into<JsValue> + crate::sys::Promising,
235    <T as crate::sys::Promising>::Resolution: EncodeTypeDef,
236    E: Into<JsValue>,
237{
238    #[inline]
239    fn into_js_result(self) -> Result<JsValue, JsValue> {
240        match self {
241            Ok(value) => Ok(value.into()),
242            Err(error) => Err(error.into()),
243        }
244    }
245}
246
247/// Reconstructs the declared return type of an `async` import from the
248/// `Result<JsValue, JsValue>` a settled JS promise yields. A `Result<T, E>`
249/// return propagates a rejection as `Err`; any other return type panics on
250/// rejection. `Result` is dispatched by type, so it is seen through aliases.
251pub trait FromJsFuture: Sized {
252    fn from_js_future(result: Result<JsValue, JsValue>) -> Self;
253}
254
255impl<T: TryFromJsValue> FromJsFuture for T {
256    #[inline]
257    fn from_js_future(result: Result<JsValue, JsValue>) -> Self {
258        let value = result.expect("async function failed");
259        T::try_from_js_value(value).expect("async function returned incompatible value")
260    }
261}
262
263impl<T: TryFromJsValue, E: From<JsValue>> FromJsFuture for Result<T, E> {
264    #[inline]
265    fn from_js_future(result: Result<JsValue, JsValue>) -> Self {
266        match result {
267            Ok(value) => Ok(
268                T::try_from_js_value(value).expect("async function returned incompatible value")
269            ),
270            Err(error) => Err(E::from(error)),
271        }
272    }
273}
274
275/// Marker for type-safe generic upcast relationships.
276///
277/// `Null` is a present JavaScript value, so it must not model absence by
278/// upcasting into [`JsOption`](crate::sys::JsOption):
279///
280/// ```compile_fail
281/// use wry_bindgen::convert::UpcastFrom;
282/// use wry_bindgen::sys::{JsOption, Null};
283/// use wry_bindgen::JsValue;
284///
285/// fn assert_upcast<S, T>()
286/// where
287///     T: UpcastFrom<S>,
288/// {
289/// }
290///
291/// assert_upcast::<Null, JsOption<JsValue>>();
292/// ```
293///
294/// Mutable references are invariant, so widening `&mut T` to `&mut Target`
295/// requires both directions to be valid:
296///
297/// ```compile_fail
298/// use wry_bindgen::convert::UpcastFrom;
299///
300/// struct Specific;
301/// struct General;
302///
303/// impl UpcastFrom<Specific> for General {}
304///
305/// fn assert_upcast<S, T: ?Sized>()
306/// where
307///     T: UpcastFrom<S>,
308/// {
309/// }
310///
311/// assert_upcast::<&mut Specific, &mut General>();
312/// ```
313pub trait UpcastFrom<S: ?Sized> {}
314
315/// Type-safe generic upcast helper.
316pub trait Upcast<T: ?Sized> {
317    #[inline]
318    fn upcast(&self) -> &T
319    where
320        Self: crate::__rt::marker::ErasableGeneric,
321        T: Sized
322            + crate::__rt::marker::ErasableGeneric<
323                Repr = <Self as crate::__rt::marker::ErasableGeneric>::Repr,
324            >,
325    {
326        unsafe { &*(self as *const Self as *const T) }
327    }
328
329    #[inline]
330    fn upcast_into(self) -> T
331    where
332        Self: Sized + crate::__rt::marker::ErasableGeneric,
333        T: Sized
334            + crate::__rt::marker::ErasableGeneric<
335                Repr = <Self as crate::__rt::marker::ErasableGeneric>::Repr,
336            >,
337    {
338        unsafe { core::mem::transmute_copy(&core::mem::ManuallyDrop::new(self)) }
339    }
340}
341
342impl<S, T> Upcast<T> for S
343where
344    T: UpcastFrom<S> + ?Sized,
345    S: ?Sized,
346{
347}
348
349impl<'a, T: ?Sized, Target: ?Sized> UpcastFrom<&'a mut T> for &'a mut Target
350where
351    Target: UpcastFrom<T>,
352    T: UpcastFrom<Target>,
353{
354}
355impl<'a, T, Target> UpcastFrom<&'a T> for &'a Target where Target: UpcastFrom<T> {}
356
357macro_rules! impl_tuple_upcast {
358    ([$($ty:ident)+] [$($target:ident)+]) => {
359        impl<$($ty,)+ $($target,)+> UpcastFrom<($($ty,)+)> for ($($target,)+)
360        where
361            $($ty: JsGeneric,)+
362            $($target: JsGeneric + UpcastFrom<$ty>,)+
363        {
364        }
365
366        impl<$($ty,)+ $($target,)+> UpcastFrom<($($ty,)+)> for crate::sys::JsOption<($($target,)+)>
367        where
368            $($ty: JsGeneric,)+
369            $($target: JsGeneric + UpcastFrom<$ty>,)+
370        {
371        }
372    };
373}
374
375impl_tuple_upcast!([T1][Target1]);
376impl_tuple_upcast!([T1 T2] [Target1 Target2]);
377impl_tuple_upcast!([T1 T2 T3] [Target1 Target2 Target3]);
378impl_tuple_upcast!([T1 T2 T3 T4] [Target1 Target2 Target3 Target4]);
379impl_tuple_upcast!([T1 T2 T3 T4 T5] [Target1 Target2 Target3 Target4 Target5]);
380impl_tuple_upcast!([T1 T2 T3 T4 T5 T6] [Target1 Target2 Target3 Target4 Target5 Target6]);
381impl_tuple_upcast!([T1 T2 T3 T4 T5 T6 T7] [Target1 Target2 Target3 Target4 Target5 Target6 Target7]);
382impl_tuple_upcast!([T1 T2 T3 T4 T5 T6 T7 T8] [Target1 Target2 Target3 Target4 Target5 Target6 Target7 Target8]);
383
384/// Convenience bound for JS values whose generic parameters erase to `JsValue`.
385pub trait JsGeneric:
386    crate::__rt::marker::ErasableGeneric<Repr = JsValue>
387    + UpcastFrom<Self>
388    + Upcast<Self>
389    + Upcast<JsValue>
390    + JsCast
391    + crate::__rt::JsRefEncode
392    + crate::__rt::EncodeTypeDef
393    + crate::__rt::BinaryEncode
394    + crate::__rt::BinaryDecode
395    + crate::__rt::BatchableResult
396    + 'static
397{
398}
399
400impl<T> JsGeneric for T where
401    T: crate::__rt::marker::ErasableGeneric<Repr = JsValue>
402        + UpcastFrom<T>
403        + Upcast<JsValue>
404        + JsCast
405        + crate::__rt::JsRefEncode
406        + crate::__rt::EncodeTypeDef
407        + crate::__rt::BinaryEncode
408        + crate::__rt::BinaryDecode
409        + crate::__rt::BatchableResult
410        + 'static
411{
412}
413
414/// Converts a value into its canonical JS-generic representation.
415pub trait IntoJsGeneric {
416    type JsCanon: JsGeneric;
417
418    fn to_js(self) -> Self::JsCanon;
419}
420
421impl IntoJsGeneric for JsValue {
422    type JsCanon = JsValue;
423
424    #[inline]
425    fn to_js(self) -> JsValue {
426        self
427    }
428}
429
430impl<T: IntoJsGeneric + Clone> IntoJsGeneric for &T {
431    type JsCanon = T::JsCanon;
432
433    #[inline]
434    fn to_js(self) -> T::JsCanon {
435        self.clone().to_js()
436    }
437}