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);