Skip to main content

neon/types_impl/extract/
mod.rs

1//! Traits and utilities for extract Rust data from JavaScript values.
2//!
3//! The full list of included extractors can be found on [`TryFromJs`].
4//!
5//! ## Extracting Handles
6//!
7//! JavaScript arguments may be extracted into a Rust tuple.
8//!
9//! ```
10//! # use neon::{prelude::*, types::extract::*};
11//! fn greet(mut cx: FunctionContext) -> JsResult<JsString> {
12//!     let (greeting, name): (Handle<JsString>, Handle<JsString>) = cx.args()?;
13//!     let message = format!("{}, {}!", greeting.value(&mut cx), name.value(&mut cx));
14//!
15//!     Ok(cx.string(message))
16//! }
17//! ```
18//!
19//! ## Extracting Native Types
20//!
21//! It's also possible to extract directly into native Rust types instead of a [`Handle`].
22//!
23//! ```
24//! # use neon::{prelude::*, types::extract::*};
25//! fn add(mut cx: FunctionContext) -> JsResult<JsNumber> {
26//!     let (a, b): (f64, f64) = cx.args()?;
27//!
28//!     Ok(cx.number(a + b))
29//! }
30//! ```
31//!
32//! ## Extracting [`Option`]
33//!
34//! It's also possible to mix [`Handle`], Rust types, and even [`Option`] for
35//! handling `null` and `undefined`.
36//!
37//! ```
38//! # use neon::{prelude::*, types::extract::*};
39//! fn get_or_default(mut cx: FunctionContext) -> JsResult<JsValue> {
40//!     let (n, default_value): (Option<f64>, Handle<JsValue>) = cx.args()?;
41//!
42//!     if let Some(n) = n {
43//!         return Ok(cx.number(n).upcast());
44//!     }
45//!
46//!     Ok(default_value)
47//! }
48//! ```
49//!
50//! ## Additional Extractors
51//!
52//! In some cases, the expected JavaScript type is ambiguous. For example, when
53//! trying to extract an [`f64`], the argument may be a `Date` instead of a `number`.
54//! Newtype extractors are provided to help.
55//!
56//! ```
57//! # use neon::{prelude::*, types::extract::*};
58//! # #[cfg(feature = "napi-5")]
59//! # use neon::types::JsDate;
60//!
61//! # #[cfg(feature = "napi-5")]
62//! fn add_hours(mut cx: FunctionContext) -> JsResult<JsDate> {
63//!     const MS_PER_HOUR: f64 = 60.0 * 60.0 * 1000.0;
64//!
65//!     let (Date(date), hours): (Date, f64) = cx.args()?;
66//!     let date = date + hours * MS_PER_HOUR;
67//!
68//!     cx.date(date).or_throw(&mut cx)
69//! }
70//! ```
71//!
72//! ## Overloaded Functions
73//!
74//! It's common in JavaScript to overload function signatures. This can be implemented with
75//! [`FunctionContext::args_opt`] or [`Context::try_catch`].
76//!
77//! ```
78//! # use neon::{prelude::*, types::extract::*};
79//!
80//! fn add(mut cx: FunctionContext, a: f64, b: f64) -> Handle<JsNumber> {
81//!     cx.number(a + b)
82//! }
83//!
84//! fn concat(mut cx: FunctionContext, a: String, b: String) -> Handle<JsString> {
85//!     cx.string(a + &b)
86//! }
87//!
88//! fn combine(mut cx: FunctionContext) -> JsResult<JsValue> {
89//!     if let Some((a, b)) = cx.args_opt()? {
90//!         return Ok(add(cx, a, b).upcast());
91//!     }
92//!
93//!     let (a, b) = cx.args()?;
94//!
95//!     Ok(concat(cx, a, b).upcast())
96//! }
97//! ```
98//!
99//! Note well, in this example, type annotations are not required on the tuple because
100//! Rust is able to infer it from the type arguments on `add` and `concat`.
101
102use crate::{
103    context::{Context, Cx, FunctionContext},
104    handle::Handle,
105    result::{JsResult, NeonResult},
106    types::{JsValue, Value},
107};
108
109pub use self::{
110    array::{Array, ArrayError},
111    boxed::Boxed,
112    buffer::{
113        ArrayBuffer, BigInt64Array, BigUint64Array, Buffer, Float32Array, Float64Array, Int16Array,
114        Int32Array, Int8Array, Uint16Array, Uint32Array, Uint8Array,
115    },
116    error::{Error, TypeExpected},
117    with::with,
118};
119
120#[cfg(feature = "serde")]
121#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
122pub use self::json::Json;
123
124#[cfg(feature = "serde")]
125#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
126pub mod json;
127
128mod array;
129mod boxed;
130mod buffer;
131mod container;
132mod either;
133mod error;
134pub(crate) mod private;
135mod try_from_js;
136mod try_into_js;
137mod with;
138
139/// Extract Rust data from a JavaScript value
140pub trait TryFromJs<'cx>
141where
142    Self: private::Sealed + Sized,
143{
144    type Error: TryIntoJs<'cx>;
145
146    /// Extract this Rust type from a JavaScript value
147    fn try_from_js(
148        cx: &mut Cx<'cx>,
149        v: Handle<'cx, JsValue>,
150    ) -> NeonResult<Result<Self, Self::Error>>;
151
152    /// Same as [`TryFromJs`], but all errors are converted to JavaScript exceptions
153    fn from_js(cx: &mut Cx<'cx>, v: Handle<'cx, JsValue>) -> NeonResult<Self> {
154        match Self::try_from_js(cx, v)? {
155            Ok(v) => Ok(v),
156            Err(err) => {
157                let err = err.try_into_js(cx)?;
158
159                cx.throw(err)
160            }
161        }
162    }
163}
164
165/// Convert Rust data into a JavaScript value
166pub trait TryIntoJs<'cx>
167where
168    Self: private::Sealed,
169{
170    /// The type of JavaScript value that will be created
171    type Value: Value;
172
173    /// Convert `self` into a JavaScript value
174    fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value>;
175}
176
177/// Extract a borrowed reference to Rust data from a JavaScript value
178///
179/// This trait is similar to [`TryFromJs`], but instead of extracting an owned value,
180/// it returns a guard that dereferences to a borrowed reference. This is useful for
181/// efficiently passing class instances by reference in method calls.
182///
183/// # Example
184///
185/// ```ignore
186/// // In a class method, accept another instance by reference
187/// pub fn distance(&self, other: &Point) -> f64 {
188///     // other is borrowed, not cloned
189/// }
190/// ```
191///
192/// The macro will automatically use `TryFromJsRef` when it sees `&T` parameters.
193pub trait TryFromJsRef<'cx>
194where
195    Self: private::Sealed + Sized,
196{
197    /// A guard type that dereferences to `&Self` and keeps the borrow alive
198    type Guard: std::ops::Deref<Target = Self>;
199
200    /// The error type returned when extraction fails
201    type Error: TryIntoJs<'cx>;
202
203    /// Extract a borrowed reference from a JavaScript value
204    fn try_from_js_ref(
205        cx: &mut Cx<'cx>,
206        v: Handle<'cx, JsValue>,
207    ) -> NeonResult<Result<Self::Guard, Self::Error>>;
208
209    /// Same as [`TryFromJsRef::try_from_js_ref`], but all errors are converted to JavaScript exceptions
210    fn from_js_ref(cx: &mut Cx<'cx>, v: Handle<'cx, JsValue>) -> NeonResult<Self::Guard> {
211        match Self::try_from_js_ref(cx, v)? {
212            Ok(guard) => Ok(guard),
213            Err(err) => {
214                let err = err.try_into_js(cx)?;
215                cx.throw(err)
216            }
217        }
218    }
219}
220
221/// Extract a mutable borrowed reference to Rust data from a JavaScript value
222///
223/// This trait is similar to [`TryFromJsRef`], but returns a guard that allows
224/// mutable access. This is useful for passing class instances by mutable reference
225/// in method calls.
226///
227/// # Example
228///
229/// ```ignore
230/// // In a class method, accept another instance by mutable reference
231/// pub fn swap_coordinates(&mut self, other: &mut Point) {
232///     std::mem::swap(&mut self.x, &mut other.x);
233///     std::mem::swap(&mut self.y, &mut other.y);
234/// }
235/// ```
236///
237/// The macro will automatically use `TryFromJsRefMut` when it sees `&mut T` parameters.
238pub trait TryFromJsRefMut<'cx>
239where
240    Self: private::Sealed + Sized,
241{
242    /// A guard type that dereferences to `&mut Self` and keeps the mutable borrow alive
243    type Guard: std::ops::DerefMut<Target = Self>;
244
245    /// The error type returned when extraction fails
246    type Error: TryIntoJs<'cx>;
247
248    /// Extract a mutable borrowed reference from a JavaScript value
249    fn try_from_js_ref_mut(
250        cx: &mut Cx<'cx>,
251        v: Handle<'cx, JsValue>,
252    ) -> NeonResult<Result<Self::Guard, Self::Error>>;
253
254    /// Same as [`TryFromJsRefMut::try_from_js_ref_mut`], but all errors are converted to JavaScript exceptions
255    fn from_js_ref_mut(cx: &mut Cx<'cx>, v: Handle<'cx, JsValue>) -> NeonResult<Self::Guard> {
256        match Self::try_from_js_ref_mut(cx, v)? {
257            Ok(guard) => Ok(guard),
258            Err(err) => {
259                let err = err.try_into_js(cx)?;
260                cx.throw(err)
261            }
262        }
263    }
264}
265
266#[cfg_attr(docsrs, doc(cfg(feature = "napi-5")))]
267#[cfg(feature = "napi-5")]
268/// Wrapper for converting between [`f64`] and [`JsDate`](super::JsDate)
269pub struct Date(pub f64);
270
271/// Trait specifying values that may be extracted from function arguments.
272///
273/// **Note:** This trait is implemented for tuples of up to 32 values, but for
274/// the sake of brevity, only tuples up to size 8 are shown in this documentation.
275pub trait FromArgs<'cx>: private::FromArgsInternal<'cx> {}
276
277// Functions as a noop zero-argument base case of [`from_args_impl`]
278impl<'cx> private::FromArgsInternal<'cx> for () {
279    fn from_args(_cx: &mut FunctionContext<'cx>) -> NeonResult<Self> {
280        Ok(())
281    }
282
283    fn from_args_opt(_cx: &mut FunctionContext<'cx>) -> NeonResult<Option<Self>> {
284        Ok(Some(()))
285    }
286}
287
288// Functions as a zero-argument base case of [`from_args_impl`]
289impl<'cx> FromArgs<'cx> for () {}
290
291// N.B.: `FromArgs` _could_ have a blanket impl for `T` where `T: FromArgsInternal`.
292// However, it is explicitly implemented in the macro in order for it to be included in docs.
293macro_rules! from_args_impl {
294    ($(#[$attrs:meta])? [$($ty:ident),*]) => {
295        $(#[$attrs])?
296        impl<'cx, $($ty,)*> FromArgs<'cx> for ($($ty,)*)
297        where
298            $($ty: TryFromJs<'cx>,)*
299        {}
300
301        #[allow(non_snake_case)]
302        impl<'cx, $($ty,)*> private::FromArgsInternal<'cx> for ($($ty,)*)
303        where
304            $($ty: TryFromJs<'cx>,)*
305        {
306            fn from_args(cx: &mut FunctionContext<'cx>) -> NeonResult<Self> {
307                let [$($ty,)*] = cx.argv();
308
309                Ok(($($ty::from_js(cx, $ty)?,)*))
310            }
311
312            fn from_args_opt(cx: &mut FunctionContext<'cx>) -> NeonResult<Option<Self>> {
313                let [$($ty,)*] = cx.argv();
314
315                Ok(Some((
316                    $(match $ty::try_from_js(cx, $ty)? {
317                        Ok(v) => v,
318                        Err(_) => return Ok(None),
319                    },)*
320                )))
321            }
322        }
323    }
324}
325
326macro_rules! from_args_expand {
327    ($(#[$attrs:meta])? [$($head:ident),*], []) => {};
328
329    ($(#[$attrs:meta])? [$($head:ident),*], [$cur:ident $(, $tail:ident)*]) => {
330        from_args_impl!($(#[$attrs])? [$($head,)* $cur]);
331        from_args_expand!($(#[$attrs])? [$($head,)* $cur], [$($tail),*]);
332    };
333}
334
335macro_rules! from_args {
336    ([$($show:ident),*], [$($hide:ident),*]) => {
337        from_args_expand!([], [$($show),*]);
338        from_args_expand!(#[doc(hidden)] [$($show),*], [$($hide),*]);
339    };
340}
341
342// Implement `FromArgs` for tuples up to length `32`. The first list is included
343// in docs and the second list is `#[doc(hidden)]`.
344from_args!(
345    [T1, T2, T3, T4, T5, T6, T7, T8],
346    [
347        T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26,
348        T27, T28, T29, T30, T31, T32
349    ]
350);