nodex_api/value/
external.rs

1use crate::{api, prelude::*};
2use std::{marker::PhantomData, mem::MaybeUninit, os::raw::c_void};
3
4#[derive(Copy, Clone, Debug)]
5pub struct JsExternal<T>(pub(crate) JsValue, PhantomData<T>);
6
7impl<T> JsExternal<T> {
8    pub(crate) fn from_value(value: JsValue) -> JsExternal<T> {
9        JsExternal(value, PhantomData)
10    }
11
12    /// This API allocates a JavaScript value with external data attached to it. This is used to
13    /// pass external data through JavaScript code, so it can be retrieved later by native code
14    /// using napi_get_value_external.
15    pub fn new(
16        env: NapiEnv,
17        value: T,
18        finalizer: impl FnOnce(NapiEnv, T) -> NapiResult<()> + 'static,
19    ) -> NapiResult<JsExternal<T>> {
20        type FnOnceBoxed<T> = Box<dyn FnOnce(NapiEnv, T) -> NapiResult<()>>;
21        // NB: first leak value.
22        let value = Box::into_raw(Box::new(value));
23
24        unsafe extern "C" fn finalize<T>(env: NapiEnv, data: DataPointer, hint: DataPointer) {
25            let ext: Box<T> = Box::from_raw(data as *mut T);
26            let finalizer: Box<FnOnceBoxed<T>> = Box::from_raw(hint as _);
27            if let Err(e) = finalizer(env, *ext) {
28                log::error!("JsExternal::<T>::finalize: {}", e);
29            }
30        }
31
32        let finalizer: Box<FnOnceBoxed<T>> = Box::new(Box::new(finalizer));
33
34        let value = napi_call!(
35            =napi_create_external,
36            env,
37            value as *mut c_void,
38            Some(finalize::<T>),
39            Box::into_raw(finalizer) as DataPointer,
40        );
41
42        Ok(JsExternal(JsValue::from_raw(env, value), PhantomData))
43    }
44
45    /// Access the underlaying data.
46    pub fn get(&self) -> NapiResult<&mut T> {
47        let ext = napi_call!(=napi_get_value_external, self.env(), self.raw());
48        unsafe { Ok(&mut *(ext as *mut T)) }
49    }
50
51    /// This API returns a Node-API value corresponding to a JavaScript ArrayBuffer. The underlying byte buffer of the ArrayBuffer is externally allocated and managed. The caller must ensure that the byte buffer remains valid until the finalize callback is called.
52    /// The API adds a napi_finalize callback which will be called when the JavaScript object just created is ready for garbage collection. It is similar to napi_wrap() except that:
53    /// - the native data cannot be retrieved later using napi_unwrap(),
54    /// - nor can it be removed later using napi_remove_wrap(), and
55    /// - the object created by the API can be used with napi_wrap().
56    /// JavaScript ArrayBuffers are described in Section 24.1 of the ECMAScript Language Specification.
57    pub fn arraybuffer<'a>(
58        env: NapiEnv,
59        value: impl AsRef<[T]>,
60    ) -> NapiResult<JsExternal<&'a [T]>> {
61        todo!()
62    }
63
64    /// This API allocates a node::Buffer object and initializes it with data backed by the passed in buffer. While this is still a fully-supported data structure, in most cases using a TypedArray will suffice.
65    /// The API adds a napi_finalize callback which will be called when the JavaScript object just created is ready for garbage collection. It is similar to napi_wrap() except that:
66    /// - the native data cannot be retrieved later using napi_unwrap(),
67    /// - nor can it be removed later using napi_remove_wrap(), and
68    /// - the object created by the API can be used with napi_wrap().
69    /// For Node.js >=4 Buffers are Uint8Arrays.
70    pub fn buffer<'a>(env: NapiEnv, value: impl AsRef<[T]>) -> NapiResult<JsExternal<&'a [T]>> {
71        todo!()
72    }
73}
74
75impl<T> NapiValueT for JsExternal<T> {
76    fn from_raw(env: NapiEnv, raw: napi_value) -> JsExternal<T> {
77        JsExternal(JsValue(env, raw), PhantomData)
78    }
79
80    fn value(&self) -> JsValue {
81        self.0
82    }
83}
84
85impl<T> NapiValueCheck for JsExternal<T> {
86    fn check(&self) -> NapiResult<bool> {
87        Ok(self.kind()? == NapiValuetype::External)
88    }
89}