Skip to main content

neon/types_impl/extract/
try_from_js.rs

1// Implementations in this file are equivalent to a call to `.downcast()` and
2// `.value(&mut cx)`. These specialized versions provide a performance benefit
3// because they can combine two Node-API calls into a single call that both
4// gets the value and checks the type at the same time.
5
6use std::{convert::Infallible, ptr};
7
8use crate::{
9    context::{internal::ContextInternal, Cx},
10    handle::{Handle, Root},
11    object::Object,
12    result::{NeonResult, Throw},
13    sys,
14    types::{
15        extract::{TryFromJs, TypeExpected},
16        private::ValueInternal,
17        JsBoolean, JsNumber, JsString, JsValue, Value,
18    },
19};
20
21#[cfg(feature = "napi-5")]
22use crate::types::{extract::Date, JsDate};
23
24impl<'cx, V> TryFromJs<'cx> for Handle<'cx, V>
25where
26    V: Value,
27{
28    type Error = TypeExpected<V>;
29
30    fn try_from_js(
31        cx: &mut Cx<'cx>,
32        v: Handle<'cx, JsValue>,
33    ) -> NeonResult<Result<Self, Self::Error>> {
34        Ok(v.downcast(cx).map_err(|_| TypeExpected::new()))
35    }
36}
37
38impl<'cx, O> TryFromJs<'cx> for Root<O>
39where
40    O: Object,
41{
42    type Error = TypeExpected<O>;
43
44    fn try_from_js(
45        cx: &mut Cx<'cx>,
46        v: Handle<'cx, JsValue>,
47    ) -> NeonResult<Result<Self, Self::Error>> {
48        Ok(match v.downcast::<O, _>(cx) {
49            Ok(v) => Ok(v.root(cx)),
50            Err(_) => Err(TypeExpected::new()),
51        })
52    }
53}
54
55impl<'cx, T> TryFromJs<'cx> for Option<T>
56where
57    T: TryFromJs<'cx>,
58{
59    type Error = T::Error;
60
61    fn try_from_js(
62        cx: &mut Cx<'cx>,
63        v: Handle<'cx, JsValue>,
64    ) -> NeonResult<Result<Self, Self::Error>> {
65        if is_null_or_undefined(cx, v)? {
66            return Ok(Ok(None));
67        }
68
69        T::try_from_js(cx, v).map(|v| v.map(Some))
70    }
71}
72
73impl<'cx> TryFromJs<'cx> for f64 {
74    type Error = TypeExpected<JsNumber>;
75
76    fn try_from_js(
77        cx: &mut Cx<'cx>,
78        v: Handle<'cx, JsValue>,
79    ) -> NeonResult<Result<Self, Self::Error>> {
80        let mut n = 0f64;
81
82        unsafe {
83            match sys::get_value_double(cx.env().to_raw(), v.to_local(), &mut n) {
84                Err(sys::Status::NumberExpected) => return Ok(Err(TypeExpected::new())),
85                Err(sys::Status::PendingException) => return Err(Throw::new()),
86                status => status.unwrap(),
87            };
88        }
89
90        Ok(Ok(n))
91    }
92}
93
94impl<'cx> TryFromJs<'cx> for u32 {
95    type Error = TypeExpected<JsNumber>;
96
97    fn try_from_js(
98        cx: &mut Cx<'cx>,
99        v: Handle<'cx, JsValue>,
100    ) -> NeonResult<Result<Self, Self::Error>> {
101        let mut n = 0u32;
102
103        unsafe {
104            match sys::get_value_uint32(cx.env().to_raw(), v.to_local(), &mut n) {
105                Err(sys::Status::NumberExpected) => return Ok(Err(TypeExpected::new())),
106                Err(sys::Status::PendingException) => return Err(Throw::new()),
107                status => status.unwrap(),
108            };
109        }
110
111        Ok(Ok(n))
112    }
113}
114
115impl<'cx> TryFromJs<'cx> for i32 {
116    type Error = TypeExpected<JsNumber>;
117
118    fn try_from_js(
119        cx: &mut Cx<'cx>,
120        v: Handle<'cx, JsValue>,
121    ) -> NeonResult<Result<Self, Self::Error>> {
122        let mut n = 0i32;
123
124        unsafe {
125            match sys::get_value_int32(cx.env().to_raw(), v.to_local(), &mut n) {
126                Err(sys::Status::NumberExpected) => return Ok(Err(TypeExpected::new())),
127                Err(sys::Status::PendingException) => return Err(Throw::new()),
128                status => status.unwrap(),
129            };
130        }
131
132        Ok(Ok(n))
133    }
134}
135
136impl<'cx> TryFromJs<'cx> for bool {
137    type Error = TypeExpected<JsBoolean>;
138
139    fn try_from_js(
140        cx: &mut Cx<'cx>,
141        v: Handle<'cx, JsValue>,
142    ) -> NeonResult<Result<Self, Self::Error>> {
143        let mut b = false;
144
145        unsafe {
146            match sys::get_value_bool(cx.env().to_raw(), v.to_local(), &mut b) {
147                Err(sys::Status::BooleanExpected) => return Ok(Err(TypeExpected::new())),
148                Err(sys::Status::PendingException) => return Err(Throw::new()),
149                status => status.unwrap(),
150            };
151        }
152
153        Ok(Ok(b))
154    }
155}
156
157impl<'cx> TryFromJs<'cx> for String {
158    type Error = TypeExpected<JsString>;
159
160    fn try_from_js(
161        cx: &mut Cx<'cx>,
162        v: Handle<'cx, JsValue>,
163    ) -> NeonResult<Result<Self, Self::Error>> {
164        let env = cx.env().to_raw();
165        let v = v.to_local();
166        let mut len = 0usize;
167
168        unsafe {
169            match sys::get_value_string_utf8(env, v, ptr::null_mut(), 0, &mut len) {
170                Err(sys::Status::StringExpected) => return Ok(Err(TypeExpected::new())),
171                Err(sys::Status::PendingException) => return Err(Throw::new()),
172                status => status.unwrap(),
173            };
174        }
175
176        // Make room for null terminator to avoid losing a character
177        let mut buf = Vec::<u8>::with_capacity(len + 1);
178        let mut written = 0usize;
179
180        unsafe {
181            assert_eq!(
182                sys::get_value_string_utf8(
183                    env,
184                    v,
185                    buf.as_mut_ptr().cast(),
186                    buf.capacity(),
187                    &mut written,
188                ),
189                Ok(())
190            );
191
192            debug_assert_eq!(len, written);
193            buf.set_len(len);
194
195            Ok(Ok(String::from_utf8_unchecked(buf)))
196        }
197    }
198}
199
200#[cfg_attr(docsrs, doc(cfg(feature = "napi-5")))]
201#[cfg(feature = "napi-5")]
202impl<'cx> TryFromJs<'cx> for Date {
203    type Error = TypeExpected<JsDate>;
204
205    fn try_from_js(
206        cx: &mut Cx<'cx>,
207        v: Handle<'cx, JsValue>,
208    ) -> NeonResult<Result<Self, Self::Error>> {
209        let mut d = 0f64;
210
211        unsafe {
212            match sys::get_date_value(cx.env().to_raw(), v.to_local(), &mut d) {
213                Err(sys::Status::DateExpected) => return Ok(Err(TypeExpected::new())),
214                Err(sys::Status::PendingException) => return Err(Throw::new()),
215                status => status.unwrap(),
216            };
217        }
218
219        Ok(Ok(Date(d)))
220    }
221}
222
223// This implementation primarily exists for macro authors. It is infallible, rather
224// than checking a type, to match the JavaScript conventions of ignoring additional
225// arguments.
226//
227// N.B.: There is a blanket impl of `FromArgs` for `T` where `T: TryFromJs` to make
228// the common case of `arity == 1` more ergonomic and avoid `(T)` is *not* a tuple
229// foot-gun (but, `(T,)` is). This creates ambiguity for `()`. Are we extracting
230// unit from the first argument of a function with `arity == 1` or is this a function
231// with `arity == 0`? By making extraction of unit infallible, we eliminate any
232// impact from the ambiguity.
233impl<'cx> TryFromJs<'cx> for () {
234    type Error = Infallible;
235
236    fn try_from_js(
237        _cx: &mut Cx<'cx>,
238        _v: Handle<'cx, JsValue>,
239    ) -> NeonResult<Result<Self, Self::Error>> {
240        Ok(Ok(()))
241    }
242}
243
244fn is_null_or_undefined<V>(cx: &mut Cx, v: Handle<V>) -> NeonResult<bool>
245where
246    V: Value,
247{
248    let mut ty = sys::ValueType::Object;
249
250    unsafe {
251        match sys::typeof_value(cx.env().to_raw(), v.to_local(), &mut ty) {
252            Err(sys::Status::PendingException) => return Err(Throw::new()),
253            status => status.unwrap(),
254        };
255    }
256
257    Ok(matches!(
258        ty,
259        sys::ValueType::Undefined | sys::ValueType::Null,
260    ))
261}