mirror of
https://github.com/napi-rs/napi-rs.git
synced 2025-12-08 19:56:07 +00:00
feat(napi): impl ReadableStream and AsyncGenerator (#2418)
* feat(napi): impl ReadableStream and AsyncGenerator * clippy fix * Into<Vec<u8>> trait * Skip node18 stream test due to Node.js bug * Cleanup * Also skip wasi test * Merge test * Skip wasi * Useless expect-error
This commit is contained in:
parent
aad71cdb00
commit
98cb7671d3
@ -17,11 +17,11 @@ impl Task for BufferLength {
|
||||
}
|
||||
|
||||
#[js_function(1)]
|
||||
fn bench_async_task(ctx: CallContext) -> Result<PromiseRaw<u32>> {
|
||||
fn bench_async_task(ctx: CallContext) -> Result<Unknown> {
|
||||
let n = ctx.get::<Buffer>(0)?;
|
||||
let task = BufferLength(n);
|
||||
let async_promise = ctx.env.spawn(task)?;
|
||||
Ok(async_promise.promise_object())
|
||||
Ok(async_promise.promise_object().into_unknown())
|
||||
}
|
||||
|
||||
#[js_function(2)]
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { build} from 'esbuild'
|
||||
import { pull } from 'lodash-es'
|
||||
|
||||
import packageJson from './package.json' assert { type: 'json' }
|
||||
import packageJson from './package.json' with { type: 'json' }
|
||||
|
||||
await build({
|
||||
entryPoints: ['./dist/index.js'],
|
||||
|
||||
@ -418,7 +418,7 @@ impl NapiStruct {
|
||||
return quote! {};
|
||||
}
|
||||
quote! {
|
||||
napi::__private::create_iterator::<#name>(env, instance_value, wrapped_value);
|
||||
unsafe { napi::__private::create_iterator::<#name>(env, instance_value, wrapped_value); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -190,6 +190,7 @@ static KNOWN_TYPES: LazyLock<HashMap<&'static str, (&'static str, bool, bool)>>
|
||||
("ClassInstance", ("{}", false, false)),
|
||||
("Function", ("({}) => {}", true, false)),
|
||||
("FunctionRef", ("({}) => {}", true, false)),
|
||||
("ReadableStream", ("ReadableStream<{}>", false, false)),
|
||||
("Either", ("{} | {}", false, true)),
|
||||
("Either3", ("{} | {} | {}", false, true)),
|
||||
("Either4", ("{} | {} | {} | {}", false, true)),
|
||||
@ -507,7 +508,7 @@ pub fn ty_to_ts_type(
|
||||
});
|
||||
|
||||
// Generic type handling
|
||||
if args.len() > 0 {
|
||||
if !args.is_empty() {
|
||||
let arg_str = args
|
||||
.iter()
|
||||
.map(|(arg, _)| arg.clone())
|
||||
@ -515,10 +516,7 @@ pub fn ty_to_ts_type(
|
||||
.join(", ");
|
||||
let mut ty = rust_ty;
|
||||
if let Some((alias, _)) = type_alias {
|
||||
ty = alias
|
||||
.split_once('<')
|
||||
.and_then(|(t, _)| Some(t.to_string()))
|
||||
.unwrap();
|
||||
ty = alias.split_once('<').map(|(t, _)| t.to_string()).unwrap();
|
||||
}
|
||||
|
||||
Some((format!("{}<{}>", ty, arg_str), false))
|
||||
|
||||
@ -1464,14 +1464,14 @@ impl ConvertToAST for syn::ItemType {
|
||||
let js_name = match opts.js_name() {
|
||||
Some((name, _)) => name.to_string(),
|
||||
_ => {
|
||||
if self.generics.params.len() > 0 {
|
||||
if !self.generics.params.is_empty() {
|
||||
let types = self
|
||||
.generics
|
||||
.type_params()
|
||||
.map(|param| param.ident.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
format!("{}<{}>", self.ident.to_string(), types)
|
||||
format!("{}<{}>", self.ident, types)
|
||||
} else {
|
||||
self.ident.to_string()
|
||||
}
|
||||
|
||||
@ -40,6 +40,7 @@ napi6 = ["napi5", "napi-sys/napi6"]
|
||||
napi7 = ["napi6", "napi-sys/napi7"]
|
||||
napi8 = ["napi7", "napi-sys/napi8"]
|
||||
napi9 = ["napi8", "napi-sys/napi9"]
|
||||
web_stream = ["futures-core", "tokio-stream", "napi4", "tokio_rt"]
|
||||
noop = []
|
||||
serde-json = ["serde", "serde_json"]
|
||||
serde-json-ordered = ["serde-json", "serde_json/preserve_order"]
|
||||
@ -101,5 +102,13 @@ version = "1"
|
||||
optional = true
|
||||
version = "2"
|
||||
|
||||
[dependencies.futures-core]
|
||||
optional = true
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.tokio-stream]
|
||||
optional = true
|
||||
version = "0.1"
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = { path = "../build", version = "2.1.3" }
|
||||
|
||||
434
crates/napi/src/bindgen_runtime/async_iterator.rs
Normal file
434
crates/napi/src/bindgen_runtime/async_iterator.rs
Normal file
@ -0,0 +1,434 @@
|
||||
use std::future::Future;
|
||||
use std::ptr;
|
||||
|
||||
use crate::{
|
||||
bindgen_runtime::{FromNapiValue, Object, ToNapiValue, Unknown},
|
||||
check_status, check_status_or_throw, sys, Env, JsError, Value,
|
||||
};
|
||||
|
||||
/// Implement a Iterator for the JavaScript Class.
|
||||
/// This feature is an experimental feature and is not yet stable.
|
||||
pub trait AsyncGenerator {
|
||||
type Yield: ToNapiValue + Send + 'static;
|
||||
type Next: FromNapiValue;
|
||||
type Return: FromNapiValue;
|
||||
|
||||
/// Handle the `AsyncGenerator.next()`
|
||||
/// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator/next>
|
||||
fn next(
|
||||
&mut self,
|
||||
value: Option<Self::Next>,
|
||||
) -> impl Future<Output = crate::Result<Option<Self::Yield>>> + Send + 'static;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
/// Implement complete to handle the `AsyncGenerator.return()`
|
||||
/// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator/return>
|
||||
fn complete(
|
||||
&mut self,
|
||||
value: Option<Self::Return>,
|
||||
) -> impl Future<Output = crate::Result<Option<Self::Yield>>> + Send + 'static {
|
||||
async move { Ok(None) }
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
/// Implement catch to handle the `AsyncGenerator.throw()`
|
||||
/// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator/throw>
|
||||
fn catch(
|
||||
&mut self,
|
||||
env: Env,
|
||||
value: Unknown,
|
||||
) -> impl Future<Output = crate::Result<Option<Self::Yield>>> + Send + 'static {
|
||||
let err = value.into();
|
||||
async move { Err(err) }
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn create_async_iterator<T: AsyncGenerator>(
|
||||
env: sys::napi_env,
|
||||
instance: sys::napi_value,
|
||||
generator_ptr: *mut T,
|
||||
) {
|
||||
let mut global = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_get_global(env, &mut global) },
|
||||
"Get global object failed",
|
||||
);
|
||||
let mut symbol_object = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_get_named_property(env, global, c"Symbol".as_ptr().cast(), &mut symbol_object)
|
||||
},
|
||||
"Get global object failed",
|
||||
);
|
||||
let mut iterator_symbol = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_get_named_property(
|
||||
env,
|
||||
symbol_object,
|
||||
c"asyncIterator".as_ptr().cast(),
|
||||
&mut iterator_symbol,
|
||||
)
|
||||
},
|
||||
"Get Symbol.asyncIterator failed",
|
||||
);
|
||||
let mut generator_function = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_create_function(
|
||||
env,
|
||||
c"AsyncIterator".as_ptr().cast(),
|
||||
8,
|
||||
Some(symbol_async_generator::<T>),
|
||||
generator_ptr.cast(),
|
||||
&mut generator_function,
|
||||
)
|
||||
},
|
||||
"Create asyncIterator function failed",
|
||||
);
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_set_property(env, instance, iterator_symbol, generator_function) },
|
||||
"Failed to set Symbol.asyncIterator on class instance",
|
||||
);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub unsafe extern "C" fn symbol_async_generator<T: AsyncGenerator>(
|
||||
env: sys::napi_env,
|
||||
info: sys::napi_callback_info,
|
||||
) -> sys::napi_value {
|
||||
let mut this = ptr::null_mut();
|
||||
let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
|
||||
let mut argc = 0;
|
||||
let mut generator_ptr = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_get_cb_info(
|
||||
env,
|
||||
info,
|
||||
&mut argc,
|
||||
argv.as_mut_ptr(),
|
||||
&mut this,
|
||||
&mut generator_ptr,
|
||||
)
|
||||
},
|
||||
"Get callback info from generator function failed"
|
||||
);
|
||||
let mut generator_object = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_create_object(env, &mut generator_object) },
|
||||
"Create Generator object failed"
|
||||
);
|
||||
let mut next_function = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_create_function(
|
||||
env,
|
||||
c"next".as_ptr().cast(),
|
||||
4,
|
||||
Some(generator_next::<T>),
|
||||
generator_ptr,
|
||||
&mut next_function,
|
||||
)
|
||||
},
|
||||
"Create next function failed"
|
||||
);
|
||||
let mut return_function = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_create_function(
|
||||
env,
|
||||
c"return".as_ptr().cast(),
|
||||
6,
|
||||
Some(generator_return::<T>),
|
||||
generator_ptr,
|
||||
&mut return_function,
|
||||
)
|
||||
},
|
||||
"Create next function failed"
|
||||
);
|
||||
let mut throw_function = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_create_function(
|
||||
env,
|
||||
c"throw".as_ptr().cast(),
|
||||
5,
|
||||
Some(generator_throw::<T>),
|
||||
generator_ptr,
|
||||
&mut throw_function,
|
||||
)
|
||||
},
|
||||
"Create next function failed"
|
||||
);
|
||||
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
generator_object,
|
||||
c"next".as_ptr().cast(),
|
||||
next_function,
|
||||
)
|
||||
},
|
||||
"Set next function on Generator object failed"
|
||||
);
|
||||
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
generator_object,
|
||||
c"return".as_ptr().cast(),
|
||||
return_function,
|
||||
)
|
||||
},
|
||||
"Set return function on Generator object failed"
|
||||
);
|
||||
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
generator_object,
|
||||
c"throw".as_ptr().cast(),
|
||||
throw_function,
|
||||
)
|
||||
},
|
||||
"Set throw function on Generator object failed"
|
||||
);
|
||||
|
||||
let mut generator_state = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_get_boolean(env, false, &mut generator_state) },
|
||||
"Create generator state failed"
|
||||
);
|
||||
|
||||
generator_object
|
||||
}
|
||||
|
||||
extern "C" fn generator_next<T: AsyncGenerator>(
|
||||
env: sys::napi_env,
|
||||
info: sys::napi_callback_info,
|
||||
) -> sys::napi_value {
|
||||
match generator_next_fn::<T>(env, info) {
|
||||
Ok(value) => value,
|
||||
Err(e) => unsafe {
|
||||
let js_error: JsError = e.into();
|
||||
js_error.throw_into(env);
|
||||
ptr::null_mut()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn generator_next_fn<T: AsyncGenerator>(
|
||||
env: sys::napi_env,
|
||||
info: sys::napi_callback_info,
|
||||
) -> crate::Result<sys::napi_value> {
|
||||
let mut this = ptr::null_mut();
|
||||
let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
|
||||
let mut argc = 1;
|
||||
let mut generator_ptr = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_cb_info(
|
||||
env,
|
||||
info,
|
||||
&mut argc,
|
||||
argv.as_mut_ptr(),
|
||||
&mut this,
|
||||
&mut generator_ptr,
|
||||
)
|
||||
},
|
||||
"Get callback info from generator function failed"
|
||||
)?;
|
||||
|
||||
let g = unsafe { Box::leak(Box::from_raw(generator_ptr as *mut T)) };
|
||||
let item = if argc == 0 {
|
||||
g.next(None)
|
||||
} else {
|
||||
g.next(match unsafe { T::Next::from_napi_value(env, argv[0]) } {
|
||||
Ok(input) => Some(input),
|
||||
Err(e) => {
|
||||
unsafe {
|
||||
sys::napi_throw_error(
|
||||
env,
|
||||
format!("{}", e.status).as_ptr().cast(),
|
||||
e.reason.as_ptr().cast(),
|
||||
)
|
||||
};
|
||||
None
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let env = Env::from_raw(env);
|
||||
let promise: crate::bindgen_runtime::PromiseRaw<'_, Option<T::Yield>> = env
|
||||
.spawn_future_with_callback(item, |env, value| {
|
||||
if let Some(v) = value {
|
||||
let mut obj = Object::new(env.0)?;
|
||||
obj.set("value", v)?;
|
||||
obj.set("done", false)?;
|
||||
Ok(obj)
|
||||
} else {
|
||||
let mut obj = Object::new(env.0)?;
|
||||
obj.set("value", ())?;
|
||||
obj.set("done", true)?;
|
||||
Ok(obj)
|
||||
}
|
||||
})?;
|
||||
Ok(promise.inner)
|
||||
}
|
||||
|
||||
extern "C" fn generator_return<T: AsyncGenerator>(
|
||||
env: sys::napi_env,
|
||||
info: sys::napi_callback_info,
|
||||
) -> sys::napi_value {
|
||||
let mut this = ptr::null_mut();
|
||||
let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
|
||||
let mut argc = 1;
|
||||
let mut generator_ptr = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_get_cb_info(
|
||||
env,
|
||||
info,
|
||||
&mut argc,
|
||||
argv.as_mut_ptr(),
|
||||
&mut this,
|
||||
&mut generator_ptr,
|
||||
)
|
||||
},
|
||||
"Get callback info from generator function failed"
|
||||
);
|
||||
|
||||
let g = unsafe { Box::leak(Box::from_raw(generator_ptr as *mut T)) };
|
||||
match Env::from_raw(env).spawn_future_with_callback(
|
||||
g.complete(if argc == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(match unsafe { T::Return::from_napi_value(env, argv[0]) } {
|
||||
Ok(input) => input,
|
||||
Err(e) => {
|
||||
unsafe {
|
||||
sys::napi_throw_error(
|
||||
env,
|
||||
format!("{}", e.status).as_ptr().cast(),
|
||||
e.reason.as_ptr().cast(),
|
||||
)
|
||||
};
|
||||
return ptr::null_mut();
|
||||
}
|
||||
})
|
||||
}),
|
||||
|env, value| {
|
||||
let mut obj = Object::new(env.0)?;
|
||||
if let Some(v) = value {
|
||||
obj.set("value", v)?;
|
||||
obj.set("done", false)?;
|
||||
Ok(obj)
|
||||
} else {
|
||||
obj.set("value", ())?;
|
||||
obj.set("done", true)?;
|
||||
Ok(obj)
|
||||
}
|
||||
},
|
||||
) {
|
||||
Ok(promise) => promise.inner,
|
||||
Err(e) => {
|
||||
unsafe {
|
||||
sys::napi_throw_error(
|
||||
env,
|
||||
e.status.as_ref().as_ptr().cast(),
|
||||
e.reason.as_ptr().cast(),
|
||||
);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn generator_throw<T: AsyncGenerator>(
|
||||
env: sys::napi_env,
|
||||
info: sys::napi_callback_info,
|
||||
) -> sys::napi_value {
|
||||
let mut this = ptr::null_mut();
|
||||
let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
|
||||
let mut argc = 1;
|
||||
let mut generator_ptr = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_get_cb_info(
|
||||
env,
|
||||
info,
|
||||
&mut argc,
|
||||
argv.as_mut_ptr(),
|
||||
&mut this,
|
||||
&mut generator_ptr,
|
||||
)
|
||||
},
|
||||
"Get callback info from generator function failed"
|
||||
);
|
||||
|
||||
let g = unsafe { Box::leak(Box::from_raw(generator_ptr as *mut T)) };
|
||||
let caught = if argc == 0 {
|
||||
let mut undefined = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_get_undefined(env, &mut undefined) },
|
||||
"Get undefined failed"
|
||||
);
|
||||
g.catch(
|
||||
Env(env),
|
||||
Unknown(Value {
|
||||
env,
|
||||
value: undefined,
|
||||
value_type: crate::ValueType::Undefined,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
g.catch(
|
||||
Env(env),
|
||||
Unknown(Value {
|
||||
env,
|
||||
value: argv[0],
|
||||
value_type: crate::ValueType::Unknown,
|
||||
}),
|
||||
)
|
||||
};
|
||||
match Env::from_raw(env).spawn_future_with_callback(caught, |env, value| {
|
||||
let mut obj = Object::new(env.0)?;
|
||||
obj.set("value", value)?;
|
||||
obj.set("done", false)?;
|
||||
Ok(obj)
|
||||
}) {
|
||||
Ok(promise) => promise.inner,
|
||||
Err(e) => {
|
||||
unsafe {
|
||||
sys::napi_throw_error(
|
||||
env,
|
||||
e.status.as_ref().as_ptr().cast(),
|
||||
e.reason.as_ptr().cast(),
|
||||
);
|
||||
}
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -146,7 +146,7 @@ impl<const N: usize> CallbackInfo<N> {
|
||||
obj: T,
|
||||
) -> Result<sys::napi_value> {
|
||||
let (instance, generator_ptr) = self._construct::<IsEmptyStructHint, T>(js_name, obj)?;
|
||||
crate::__private::create_iterator(self.env, instance, generator_ptr);
|
||||
unsafe { crate::__private::create_iterator(self.env, instance, generator_ptr) };
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
@ -164,7 +164,7 @@ impl<const N: usize> CallbackInfo<N> {
|
||||
obj: T,
|
||||
) -> Result<sys::napi_value> {
|
||||
let (instance, generator_ptr) = self._factory(js_name, obj)?;
|
||||
crate::__private::create_iterator(self.env, instance, generator_ptr);
|
||||
unsafe { crate::__private::create_iterator(self.env, instance, generator_ptr) };
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use std::ffi::c_void;
|
||||
use std::ptr;
|
||||
use std::{ffi::c_void, os::raw::c_char};
|
||||
|
||||
use crate::Value;
|
||||
use crate::{bindgen_runtime::Unknown, check_status_or_throw, sys, Env};
|
||||
@ -34,8 +34,9 @@ pub trait Generator {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn create_iterator<T: Generator>(
|
||||
pub unsafe fn create_iterator<T: Generator>(
|
||||
env: sys::napi_env,
|
||||
instance: sys::napi_value,
|
||||
generator_ptr: *mut T,
|
||||
@ -288,8 +289,8 @@ extern "C" fn generator_next<T: Generator>(
|
||||
unsafe {
|
||||
sys::napi_throw_error(
|
||||
env,
|
||||
format!("{}", e.status).as_ptr() as *mut c_char,
|
||||
e.reason.as_ptr() as *mut c_char,
|
||||
format!("{}", e.status).as_ptr().cast(),
|
||||
e.reason.as_ptr().cast(),
|
||||
)
|
||||
};
|
||||
None
|
||||
@ -311,14 +312,7 @@ extern "C" fn generator_next<T: Generator>(
|
||||
);
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
result,
|
||||
c"done".as_ptr() as *const std::os::raw::c_char,
|
||||
completed_value,
|
||||
)
|
||||
},
|
||||
unsafe { sys::napi_set_named_property(env, result, c"done".as_ptr().cast(), completed_value,) },
|
||||
"Failed to set iterator result done",
|
||||
);
|
||||
|
||||
@ -359,8 +353,8 @@ extern "C" fn generator_return<T: Generator>(
|
||||
unsafe {
|
||||
sys::napi_throw_error(
|
||||
env,
|
||||
format!("{}", e.status).as_ptr() as *mut c_char,
|
||||
e.reason.as_ptr() as *mut c_char,
|
||||
format!("{}", e.status).as_ptr().cast(),
|
||||
e.reason.as_ptr().cast(),
|
||||
)
|
||||
};
|
||||
return ptr::null_mut();
|
||||
@ -395,14 +389,7 @@ extern "C" fn generator_return<T: Generator>(
|
||||
if argc > 0 {
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
result,
|
||||
c"value".as_ptr() as *const std::os::raw::c_char,
|
||||
argv[0],
|
||||
)
|
||||
},
|
||||
unsafe { sys::napi_set_named_property(env, result, c"value".as_ptr().cast(), argv[0],) },
|
||||
"Failed to set iterator result value",
|
||||
);
|
||||
}
|
||||
@ -545,14 +532,7 @@ fn set_generator_value<V: ToNapiValue>(env: sys::napi_env, result: sys::napi_val
|
||||
Ok(val) => {
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
result,
|
||||
c"value".as_ptr() as *const std::os::raw::c_char,
|
||||
val,
|
||||
)
|
||||
},
|
||||
unsafe { sys::napi_set_named_property(env, result, c"value".as_ptr().cast(), val,) },
|
||||
"Failed to set iterator result value",
|
||||
);
|
||||
}
|
||||
@ -560,8 +540,8 @@ fn set_generator_value<V: ToNapiValue>(env: sys::napi_env, result: sys::napi_val
|
||||
unsafe {
|
||||
sys::napi_throw_error(
|
||||
env,
|
||||
format!("{}", e.status).as_ptr() as *mut c_char,
|
||||
e.reason.as_ptr() as *mut c_char,
|
||||
format!("{}", e.status).as_ptr().cast(),
|
||||
e.reason.as_ptr().cast(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
@ -28,6 +28,8 @@ mod promise_raw;
|
||||
#[cfg(feature = "serde-json")]
|
||||
mod serde;
|
||||
mod set;
|
||||
#[cfg(feature = "web_stream")]
|
||||
mod stream;
|
||||
mod string;
|
||||
mod symbol;
|
||||
mod task;
|
||||
@ -50,6 +52,8 @@ pub use object::*;
|
||||
#[cfg(all(feature = "tokio_rt", feature = "napi4"))]
|
||||
pub use promise::*;
|
||||
pub use promise_raw::*;
|
||||
#[cfg(feature = "web_stream")]
|
||||
pub use stream::*;
|
||||
pub use string::*;
|
||||
pub use symbol::*;
|
||||
pub use task::*;
|
||||
|
||||
@ -103,7 +103,7 @@ impl<'scope> BufferSlice<'scope> {
|
||||
///
|
||||
/// If you need to support these runtimes, you should create a buffer by other means and then
|
||||
/// later copy the data back out.
|
||||
pub unsafe fn from_external<T: 'scope, F: FnOnce(T, Env)>(
|
||||
pub unsafe fn from_external<T: 'scope, F: FnOnce(Env, T)>(
|
||||
env: &Env,
|
||||
data: *mut u8,
|
||||
len: usize,
|
||||
@ -142,7 +142,7 @@ impl<'scope> BufferSlice<'scope> {
|
||||
let (hint, finalize) = *Box::from_raw(hint_ptr);
|
||||
let status =
|
||||
unsafe { sys::napi_create_buffer_copy(env.0, len, data.cast(), ptr::null_mut(), &mut buf) };
|
||||
finalize(hint, *env);
|
||||
finalize(*env, hint);
|
||||
status
|
||||
} else {
|
||||
status
|
||||
|
||||
@ -260,6 +260,39 @@ impl<Args: JsValuesTupleIntoVec, Return: FromNapiValue> Function<'_, Args, Retur
|
||||
)?;
|
||||
unsafe { Return::from_napi_value(self.env, raw_return) }
|
||||
}
|
||||
|
||||
/// Call `Function.bind`
|
||||
pub fn bind<T: ToNapiValue>(&self, this: T) -> Result<Function<'_, Args, Return>> {
|
||||
let raw_this = unsafe { T::to_napi_value(self.env, this) }?;
|
||||
let mut bind_function = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_named_property(self.env, self.value, c"bind".as_ptr(), &mut bind_function)
|
||||
},
|
||||
"Get bind function failed"
|
||||
)?;
|
||||
let mut bound_function = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_call_function(
|
||||
self.env,
|
||||
self.value,
|
||||
bind_function,
|
||||
1,
|
||||
[raw_this].as_ptr(),
|
||||
&mut bound_function,
|
||||
)
|
||||
},
|
||||
"Bind function failed"
|
||||
)?;
|
||||
Ok(Function {
|
||||
env: self.env,
|
||||
value: bound_function,
|
||||
_args: std::marker::PhantomData,
|
||||
_return: std::marker::PhantomData,
|
||||
_scope: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi4")]
|
||||
|
||||
@ -59,7 +59,7 @@ unsafe impl<T: FromNapiValue + Send> Send for Promise<T> {}
|
||||
impl<T: FromNapiValue> FromNapiValue for Promise<T> {
|
||||
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
|
||||
let (tx, rx) = channel();
|
||||
let mut promise_object = unsafe { PromiseRaw::<T>::from_napi_value(env, napi_val)? };
|
||||
let promise_object = unsafe { PromiseRaw::<T>::from_napi_value(env, napi_val)? };
|
||||
let tx_box = Arc::new(Cell::new(Some(tx)));
|
||||
let tx_in_catch = tx_box.clone();
|
||||
promise_object
|
||||
|
||||
@ -10,25 +10,31 @@ use crate::{
|
||||
};
|
||||
use crate::{Env, Error, NapiRaw, NapiValue, Status};
|
||||
|
||||
pub struct PromiseRaw<T> {
|
||||
use super::Unknown;
|
||||
|
||||
pub struct PromiseRaw<'env, T> {
|
||||
pub(crate) inner: sys::napi_value,
|
||||
env: sys::napi_env,
|
||||
_phantom: PhantomData<T>,
|
||||
_phantom: &'env PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> PromiseRaw<T> {
|
||||
impl<T> PromiseRaw<'_, T> {
|
||||
pub(crate) fn new(env: sys::napi_env, inner: sys::napi_value) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
env,
|
||||
_phantom: PhantomData,
|
||||
_phantom: &PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_unknown(self) -> Unknown {
|
||||
unsafe { Unknown::from_raw_unchecked(self.env, self.inner) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromNapiValue> PromiseRaw<T> {
|
||||
impl<'env, T: FromNapiValue> PromiseRaw<'env, T> {
|
||||
/// Promise.then method
|
||||
pub fn then<Callback, U>(&mut self, cb: Callback) -> Result<PromiseRaw<U>>
|
||||
pub fn then<Callback, U>(&self, cb: Callback) -> Result<PromiseRaw<'env, U>>
|
||||
where
|
||||
U: ToNapiValue,
|
||||
Callback: 'static + FnOnce(CallbackContext<T>) -> Result<U>,
|
||||
@ -88,12 +94,12 @@ impl<T: FromNapiValue> PromiseRaw<T> {
|
||||
Ok(PromiseRaw::<U> {
|
||||
env: self.env,
|
||||
inner: new_promise,
|
||||
_phantom: PhantomData,
|
||||
_phantom: &PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Promise.catch method
|
||||
pub fn catch<E, U, Callback>(&mut self, cb: Callback) -> Result<PromiseRaw<U>>
|
||||
pub fn catch<E, U, Callback>(&self, cb: Callback) -> Result<PromiseRaw<'env, U>>
|
||||
where
|
||||
E: FromNapiValue,
|
||||
U: ToNapiValue,
|
||||
@ -154,12 +160,12 @@ impl<T: FromNapiValue> PromiseRaw<T> {
|
||||
Ok(PromiseRaw::<U> {
|
||||
env: self.env,
|
||||
inner: new_promise,
|
||||
_phantom: PhantomData,
|
||||
_phantom: &PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Promise.finally method
|
||||
pub fn finally<U, Callback>(&mut self, cb: Callback) -> Result<PromiseRaw<T>>
|
||||
pub fn finally<U, Callback>(&mut self, cb: Callback) -> Result<PromiseRaw<'env, T>>
|
||||
where
|
||||
U: ToNapiValue,
|
||||
Callback: 'static + FnOnce(Env) -> Result<U>,
|
||||
@ -203,7 +209,7 @@ impl<T: FromNapiValue> PromiseRaw<T> {
|
||||
Ok(Self {
|
||||
env: self.env,
|
||||
inner: new_promise,
|
||||
_phantom: PhantomData,
|
||||
_phantom: &PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
@ -216,7 +222,7 @@ impl<T: FromNapiValue> PromiseRaw<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromNapiValue> TypeName for PromiseRaw<T> {
|
||||
impl<T: FromNapiValue> TypeName for PromiseRaw<'_, T> {
|
||||
fn type_name() -> &'static str {
|
||||
"Promise"
|
||||
}
|
||||
@ -226,7 +232,7 @@ impl<T: FromNapiValue> TypeName for PromiseRaw<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromNapiValue> ValidateNapiValue for PromiseRaw<T> {
|
||||
impl<T: FromNapiValue> ValidateNapiValue for PromiseRaw<'_, T> {
|
||||
unsafe fn validate(
|
||||
env: napi_sys::napi_env,
|
||||
napi_val: napi_sys::napi_value,
|
||||
@ -235,13 +241,13 @@ impl<T: FromNapiValue> ValidateNapiValue for PromiseRaw<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> NapiRaw for PromiseRaw<T> {
|
||||
impl<T> NapiRaw for PromiseRaw<'_, T> {
|
||||
unsafe fn raw(&self) -> sys::napi_value {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> NapiValue for PromiseRaw<T> {
|
||||
impl<T> NapiValue for PromiseRaw<'_, T> {
|
||||
unsafe fn from_raw(env: napi_sys::napi_env, value: napi_sys::napi_value) -> Result<Self> {
|
||||
let mut is_promise = false;
|
||||
check_status!(unsafe { sys::napi_is_promise(env, value, &mut is_promise) })?;
|
||||
@ -249,7 +255,7 @@ impl<T> NapiValue for PromiseRaw<T> {
|
||||
.then_some(Self {
|
||||
env,
|
||||
inner: value,
|
||||
_phantom: PhantomData,
|
||||
_phantom: &PhantomData,
|
||||
})
|
||||
.ok_or_else(|| Error::new(Status::InvalidArg, "JavaScript value is not Promise"))
|
||||
}
|
||||
@ -258,7 +264,7 @@ impl<T> NapiValue for PromiseRaw<T> {
|
||||
Self {
|
||||
env,
|
||||
inner: value,
|
||||
_phantom: PhantomData,
|
||||
_phantom: &PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
5
crates/napi/src/bindgen_runtime/js_values/stream.rs
Normal file
5
crates/napi/src/bindgen_runtime/js_values/stream.rs
Normal file
@ -0,0 +1,5 @@
|
||||
pub use read::*;
|
||||
pub use write::*;
|
||||
|
||||
mod read;
|
||||
mod write;
|
||||
545
crates/napi/src/bindgen_runtime/js_values/stream/read.rs
Normal file
545
crates/napi/src/bindgen_runtime/js_values/stream/read.rs
Normal file
@ -0,0 +1,545 @@
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
pin::Pin,
|
||||
ptr,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, RwLock,
|
||||
},
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use futures_core::Stream;
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
use crate::{
|
||||
bindgen_prelude::{
|
||||
CallbackContext, FromNapiValue, Function, PromiseRaw, ToNapiValue, TypeName, Unknown,
|
||||
ValidateNapiValue,
|
||||
},
|
||||
bindgen_runtime::{BufferSlice, Null, Object, NAPI_AUTO_LENGTH},
|
||||
check_status, sys,
|
||||
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode},
|
||||
Env, Error, JsError, NapiRaw, Result, Status, ValueType,
|
||||
};
|
||||
|
||||
pub struct ReadableStream<'env, T> {
|
||||
pub(crate) value: sys::napi_value,
|
||||
pub(crate) env: sys::napi_env,
|
||||
_marker: PhantomData<&'env T>,
|
||||
}
|
||||
|
||||
impl<T> NapiRaw for ReadableStream<'_, T> {
|
||||
unsafe fn raw(&self) -> sys::napi_value {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TypeName for ReadableStream<'_, T> {
|
||||
fn type_name() -> &'static str {
|
||||
"ReadableStream"
|
||||
}
|
||||
|
||||
fn value_type() -> ValueType {
|
||||
ValueType::Object
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ValidateNapiValue for ReadableStream<'_, T> {
|
||||
unsafe fn validate(
|
||||
env: napi_sys::napi_env,
|
||||
napi_val: napi_sys::napi_value,
|
||||
) -> Result<napi_sys::napi_value> {
|
||||
let constructor = Env::from(env)
|
||||
.get_global()?
|
||||
.get_named_property_unchecked::<Function>("ReadableStream")?;
|
||||
let mut is_instance = false;
|
||||
check_status!(
|
||||
unsafe { sys::napi_instanceof(env, napi_val, constructor.value, &mut is_instance) },
|
||||
"Check ReadableStream instance failed"
|
||||
)?;
|
||||
if !is_instance {
|
||||
return Err(Error::new(
|
||||
Status::InvalidArg,
|
||||
"Value is not a ReadableStream",
|
||||
));
|
||||
}
|
||||
Ok(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromNapiValue for ReadableStream<'_, T> {
|
||||
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
|
||||
Ok(Self {
|
||||
value: napi_val,
|
||||
env,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ReadableStream<'_, T> {
|
||||
/// Returns a boolean indicating whether or not the readable stream is locked to a reader.
|
||||
pub fn locked(&self) -> Result<bool> {
|
||||
let mut locked = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_named_property(self.env, self.value, c"locked".as_ptr().cast(), &mut locked)
|
||||
},
|
||||
"Get locked property failed"
|
||||
)?;
|
||||
unsafe { FromNapiValue::from_napi_value(self.env, locked) }
|
||||
}
|
||||
|
||||
/// The `cancel()` method of the `ReadableStream` interface returns a Promise that resolves when the stream is canceled.
|
||||
pub fn cancel(&mut self, reason: Option<String>) -> Result<PromiseRaw<()>> {
|
||||
let mut cancel_fn = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_named_property(
|
||||
self.env,
|
||||
self.value,
|
||||
c"abort".as_ptr().cast(),
|
||||
&mut cancel_fn,
|
||||
)
|
||||
},
|
||||
"Get abort property failed"
|
||||
)?;
|
||||
let reason_value = unsafe { ToNapiValue::to_napi_value(self.env, reason)? };
|
||||
let mut promise = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_call_function(
|
||||
self.env,
|
||||
self.value,
|
||||
cancel_fn,
|
||||
1,
|
||||
[reason_value].as_ptr(),
|
||||
&mut promise,
|
||||
)
|
||||
},
|
||||
"Call abort function failed"
|
||||
)?;
|
||||
Ok(PromiseRaw::new(self.env, promise))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromNapiValue> ReadableStream<'_, T> {
|
||||
pub fn read(&self) -> Result<Reader<T>> {
|
||||
let mut reader_function = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_named_property(
|
||||
self.env,
|
||||
self.value,
|
||||
c"getReader".as_ptr().cast(),
|
||||
&mut reader_function,
|
||||
)
|
||||
},
|
||||
"Get getReader on ReadableStream failed"
|
||||
)?;
|
||||
let mut reader = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_call_function(
|
||||
self.env,
|
||||
self.value,
|
||||
reader_function,
|
||||
0,
|
||||
ptr::null_mut(),
|
||||
&mut reader,
|
||||
)
|
||||
},
|
||||
"Call getReader on ReadableStreamReader failed"
|
||||
)?;
|
||||
let mut read_function = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_named_property(
|
||||
self.env,
|
||||
reader,
|
||||
c"read".as_ptr().cast(),
|
||||
&mut read_function,
|
||||
)
|
||||
},
|
||||
"Get read from ReadableStreamDefaultReader failed"
|
||||
)?;
|
||||
let mut bind_function = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_named_property(
|
||||
self.env,
|
||||
read_function,
|
||||
c"bind".as_ptr().cast(),
|
||||
&mut bind_function,
|
||||
)
|
||||
},
|
||||
"Get bind from ReadableStreamDefaultReader::read failed"
|
||||
)?;
|
||||
let mut bind_read = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_call_function(
|
||||
self.env,
|
||||
read_function,
|
||||
bind_function,
|
||||
1,
|
||||
[reader].as_ptr(),
|
||||
&mut bind_read,
|
||||
)
|
||||
},
|
||||
"Call bind from ReadableStreamDefaultReader::read failed"
|
||||
)?;
|
||||
let read_function = unsafe {
|
||||
Function::<(), PromiseRaw<IteratorValue<T>>>::from_napi_value(self.env, bind_read)?
|
||||
}
|
||||
.build_threadsafe_function()
|
||||
.callee_handled::<true>()
|
||||
.weak::<true>()
|
||||
.build()?;
|
||||
Ok(Reader {
|
||||
inner: read_function,
|
||||
state: Arc::new((RwLock::new(Ok(None)), AtomicBool::new(false))),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToNapiValue + Send + 'static> ReadableStream<'_, T> {
|
||||
pub fn new<S: Stream<Item = Result<T>> + Unpin + Send + 'static>(
|
||||
env: &Env,
|
||||
inner: S,
|
||||
) -> Result<Self> {
|
||||
let global = env.get_global()?;
|
||||
let constructor = global.get_named_property_unchecked::<Function>("ReadableStream")?;
|
||||
let mut underlying_source = Object::new(env.raw())?;
|
||||
let mut pull_fn = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_create_function(
|
||||
env.raw(),
|
||||
c"pull".as_ptr().cast(),
|
||||
NAPI_AUTO_LENGTH,
|
||||
Some(pull_callback::<T, S>),
|
||||
Box::into_raw(Box::new(inner)).cast(),
|
||||
&mut pull_fn,
|
||||
)
|
||||
},
|
||||
"Failed to create pull function"
|
||||
)?;
|
||||
underlying_source.set_named_property("pull", pull_fn)?;
|
||||
let mut stream = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_new_instance(
|
||||
env.0,
|
||||
constructor.value,
|
||||
1,
|
||||
[underlying_source.0.value].as_ptr(),
|
||||
&mut stream,
|
||||
)
|
||||
},
|
||||
"Create ReadableStream instance failed"
|
||||
)?;
|
||||
Ok(Self {
|
||||
value: stream,
|
||||
env: env.0,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'env> ReadableStream<'env, BufferSlice<'env>> {
|
||||
pub fn create_with_stream_bytes<
|
||||
B: Into<Vec<u8>>,
|
||||
S: Stream<Item = Result<B>> + Unpin + Send + 'static,
|
||||
>(
|
||||
env: &Env,
|
||||
inner: S,
|
||||
) -> Result<Self> {
|
||||
let global = env.get_global()?;
|
||||
let constructor = global.get_named_property_unchecked::<Function>("ReadableStream")?;
|
||||
let mut underlying_source = Object::new(env.raw())?;
|
||||
let mut pull_fn = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_create_function(
|
||||
env.raw(),
|
||||
c"pull".as_ptr().cast(),
|
||||
NAPI_AUTO_LENGTH,
|
||||
Some(pull_callback_bytes::<B, S>),
|
||||
Box::into_raw(Box::new(inner)).cast(),
|
||||
&mut pull_fn,
|
||||
)
|
||||
},
|
||||
"Failed to create pull function"
|
||||
)?;
|
||||
underlying_source.set_named_property("pull", pull_fn)?;
|
||||
underlying_source.set("type", "bytes")?;
|
||||
let mut stream = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_new_instance(
|
||||
env.0,
|
||||
constructor.value,
|
||||
1,
|
||||
[underlying_source.0.value].as_ptr(),
|
||||
&mut stream,
|
||||
)
|
||||
},
|
||||
"Create ReadableStream instance failed"
|
||||
)?;
|
||||
Ok(Self {
|
||||
value: stream,
|
||||
env: env.0,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IteratorValue<'env, T: FromNapiValue> {
|
||||
_marker: PhantomData<&'env ()>,
|
||||
value: Option<T>,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
impl<T: FromNapiValue> FromNapiValue for IteratorValue<'_, T> {
|
||||
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
|
||||
let mut done = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe { sys::napi_get_named_property(env, napi_val, c"done".as_ptr().cast(), &mut done) },
|
||||
"Get done property failed"
|
||||
)?;
|
||||
let done = unsafe { FromNapiValue::from_napi_value(env, done)? };
|
||||
let mut value = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe { sys::napi_get_named_property(env, napi_val, c"value".as_ptr().cast(), &mut value) },
|
||||
"Get value property failed"
|
||||
)?;
|
||||
let value = unsafe { FromNapiValue::from_napi_value(env, value)? };
|
||||
Ok(Self {
|
||||
value,
|
||||
done,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Reader<T: FromNapiValue + 'static> {
|
||||
inner: ThreadsafeFunction<(), PromiseRaw<'static, IteratorValue<'static, T>>, (), true, true>,
|
||||
state: Arc<(RwLock<Result<Option<T>>>, AtomicBool)>,
|
||||
}
|
||||
|
||||
impl<T: FromNapiValue + 'static> futures_core::Stream for Reader<T> {
|
||||
type Item = Result<T>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
if self.state.1.load(Ordering::Relaxed) {
|
||||
let mut chunk = self
|
||||
.state
|
||||
.0
|
||||
.write()
|
||||
.map_err(|_| Error::new(Status::InvalidArg, "Poisoned lock in Reader::poll_next"))?;
|
||||
let chunk = mem::replace(&mut *chunk, Ok(None))?;
|
||||
match chunk {
|
||||
Some(chunk) => return Poll::Ready(Some(Ok(chunk))),
|
||||
None => return Poll::Ready(None),
|
||||
}
|
||||
}
|
||||
let waker = cx.waker().clone();
|
||||
let state = self.state.clone();
|
||||
let state_in_catch = state.clone();
|
||||
self.inner.call_with_return_value(
|
||||
Ok(()),
|
||||
ThreadsafeFunctionCallMode::NonBlocking,
|
||||
move |iterator, _| {
|
||||
let iterator = iterator?;
|
||||
iterator
|
||||
.then(move |cx| {
|
||||
if cx.value.done {
|
||||
state.1.store(true, Ordering::Relaxed);
|
||||
}
|
||||
if let Some(val) = cx.value.value {
|
||||
let mut chunk = state.0.write().map_err(|_| {
|
||||
Error::new(Status::InvalidArg, "Poisoned lock in Reader::poll_next")
|
||||
})?;
|
||||
*chunk = Ok(Some(val));
|
||||
};
|
||||
Ok(())
|
||||
})?
|
||||
.catch(move |cx: CallbackContext<Unknown>| {
|
||||
let mut chunk = state_in_catch
|
||||
.0
|
||||
.write()
|
||||
.map_err(|_| Error::new(Status::InvalidArg, "Poisoned lock in Reader::poll_next"))?;
|
||||
let mut error_ref = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe { sys::napi_create_reference(cx.env.0, cx.value.0.value, 0, &mut error_ref) },
|
||||
"Create error reference failed"
|
||||
)?;
|
||||
*chunk = Err(Error {
|
||||
status: Status::GenericFailure,
|
||||
reason: "".to_string(),
|
||||
maybe_raw: error_ref,
|
||||
maybe_env: cx.env.0,
|
||||
raw: true,
|
||||
});
|
||||
Ok(())
|
||||
})?
|
||||
.finally(move |_| {
|
||||
waker.wake();
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(())
|
||||
},
|
||||
);
|
||||
let mut chunk = self
|
||||
.state
|
||||
.0
|
||||
.write()
|
||||
.map_err(|_| Error::new(Status::InvalidArg, "Poisoned lock in Reader::poll_next"))?;
|
||||
let chunk = mem::replace(&mut *chunk, Ok(None))?;
|
||||
match chunk {
|
||||
Some(chunk) => Poll::Ready(Some(Ok(chunk))),
|
||||
None => Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn pull_callback<
|
||||
T: ToNapiValue + Send + 'static,
|
||||
S: Stream<Item = Result<T>> + Unpin + Send + 'static,
|
||||
>(
|
||||
env: sys::napi_env,
|
||||
info: sys::napi_callback_info,
|
||||
) -> sys::napi_value {
|
||||
match pull_callback_impl::<T, S>(env, info) {
|
||||
Ok(val) => val,
|
||||
Err(err) => unsafe {
|
||||
let js_error: JsError = err.into();
|
||||
js_error.throw_into(env);
|
||||
ptr::null_mut()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn pull_callback_impl<
|
||||
T: ToNapiValue + Send + 'static,
|
||||
S: Stream<Item = Result<T>> + Unpin + Send + 'static,
|
||||
>(
|
||||
env: sys::napi_env,
|
||||
info: sys::napi_callback_info,
|
||||
) -> Result<sys::napi_value> {
|
||||
let mut data = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_cb_info(
|
||||
env,
|
||||
info,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
&mut data,
|
||||
)
|
||||
},
|
||||
"Get ReadableStream.pull callback info failed"
|
||||
)?;
|
||||
let mut stream: Pin<&mut S> = Pin::new(Box::leak(unsafe { Box::from_raw(data.cast()) }));
|
||||
let env = Env::from_raw(env);
|
||||
let promise = env.spawn_future_with_callback(
|
||||
async move { stream.next().await.transpose() },
|
||||
|env, val| {
|
||||
let mut output = Object::new(env.raw())?;
|
||||
if let Some(val) = val {
|
||||
output.set("value", val)?;
|
||||
output.set("done", false)?;
|
||||
} else {
|
||||
output.set("value", Null)?;
|
||||
output.set("done", true)?;
|
||||
}
|
||||
unsafe {
|
||||
crate::__private::log_js_value("log", env.0, [output.0.value]);
|
||||
};
|
||||
Ok(output.0.value)
|
||||
},
|
||||
)?;
|
||||
Ok(promise.inner)
|
||||
}
|
||||
|
||||
extern "C" fn pull_callback_bytes<
|
||||
B: Into<Vec<u8>>,
|
||||
S: Stream<Item = Result<B>> + Unpin + Send + 'static,
|
||||
>(
|
||||
env: sys::napi_env,
|
||||
info: sys::napi_callback_info,
|
||||
) -> sys::napi_value {
|
||||
match pull_callback_impl_bytes::<B, S>(env, info) {
|
||||
Ok(val) => val,
|
||||
Err(err) => unsafe {
|
||||
let js_error: JsError = err.into();
|
||||
js_error.throw_into(env);
|
||||
ptr::null_mut()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn pull_callback_impl_bytes<
|
||||
B: Into<Vec<u8>>,
|
||||
S: Stream<Item = Result<B>> + Unpin + Send + 'static,
|
||||
>(
|
||||
env: sys::napi_env,
|
||||
info: sys::napi_callback_info,
|
||||
) -> Result<sys::napi_value> {
|
||||
let mut data = ptr::null_mut();
|
||||
let mut argc = 1;
|
||||
let mut args = [ptr::null_mut(); 1];
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_cb_info(
|
||||
env,
|
||||
info,
|
||||
&mut argc,
|
||||
args.as_mut_ptr(),
|
||||
ptr::null_mut(),
|
||||
&mut data,
|
||||
)
|
||||
},
|
||||
"Get ReadableStream.pull callback info failed"
|
||||
)?;
|
||||
let [controller] = args;
|
||||
|
||||
let controller = unsafe { Object::from_napi_value(env, controller)? };
|
||||
let enqueue = controller
|
||||
.get_named_property_unchecked::<Function<BufferSlice, ()>>("enqueue")?
|
||||
.bind(&controller)?
|
||||
.create_ref()?;
|
||||
let close = controller
|
||||
.get_named_property_unchecked::<Function<(), ()>>("close")?
|
||||
.bind(&controller)?
|
||||
.create_ref()?;
|
||||
|
||||
let mut stream: Pin<&mut S> = Pin::new(Box::leak(unsafe { Box::from_raw(data.cast()) }));
|
||||
let env = Env::from_raw(env);
|
||||
let promise = env.spawn_future_with_callback(
|
||||
async move {
|
||||
stream
|
||||
.next()
|
||||
.await
|
||||
.transpose()
|
||||
.map(|v| v.map(|v| Into::<Vec<u8>>::into(v)))
|
||||
},
|
||||
move |env, val| {
|
||||
if let Some(val) = val {
|
||||
let enqueue_fn = enqueue.borrow_back(&env)?;
|
||||
enqueue_fn.call(BufferSlice::from_data(&env, val)?)?;
|
||||
} else {
|
||||
let close_fn = close.borrow_back(&env)?;
|
||||
close_fn.call(())?;
|
||||
}
|
||||
drop(enqueue);
|
||||
drop(close);
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
Ok(promise.inner)
|
||||
}
|
||||
143
crates/napi/src/bindgen_runtime/js_values/stream/write.rs
Normal file
143
crates/napi/src/bindgen_runtime/js_values/stream/write.rs
Normal file
@ -0,0 +1,143 @@
|
||||
use std::{marker::PhantomData, ptr};
|
||||
|
||||
use crate::{
|
||||
bindgen_prelude::{
|
||||
FromNapiValue, Function, PromiseRaw, ToNapiValue, TypeName, ValidateNapiValue,
|
||||
},
|
||||
check_status, sys, Env, Error, NapiRaw, Result, Status, ValueType,
|
||||
};
|
||||
|
||||
pub struct WriteableStream<'env> {
|
||||
pub(crate) value: sys::napi_value,
|
||||
pub(crate) env: sys::napi_env,
|
||||
pub(crate) _scope: &'env PhantomData<()>,
|
||||
}
|
||||
|
||||
impl NapiRaw for WriteableStream<'_> {
|
||||
unsafe fn raw(&self) -> sys::napi_value {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeName for WriteableStream<'_> {
|
||||
fn type_name() -> &'static str {
|
||||
"WriteableStream"
|
||||
}
|
||||
|
||||
fn value_type() -> ValueType {
|
||||
ValueType::Object
|
||||
}
|
||||
}
|
||||
|
||||
impl ValidateNapiValue for WriteableStream<'_> {
|
||||
unsafe fn validate(
|
||||
env: napi_sys::napi_env,
|
||||
napi_val: napi_sys::napi_value,
|
||||
) -> Result<napi_sys::napi_value> {
|
||||
let constructor = Env::from(env)
|
||||
.get_global()?
|
||||
.get_named_property_unchecked::<Function>("WritableStream")?;
|
||||
let mut is_instance = false;
|
||||
check_status!(
|
||||
unsafe { sys::napi_instanceof(env, napi_val, constructor.value, &mut is_instance) },
|
||||
"Check WritableStream instance failed"
|
||||
)?;
|
||||
if !is_instance {
|
||||
return Err(Error::new(
|
||||
Status::InvalidArg,
|
||||
"Value is not a WritableStream",
|
||||
));
|
||||
}
|
||||
Ok(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromNapiValue for WriteableStream<'_> {
|
||||
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
|
||||
Ok(Self {
|
||||
value: napi_val,
|
||||
env,
|
||||
_scope: &PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteableStream<'_> {
|
||||
pub fn ready(&self) -> Result<PromiseRaw<()>> {
|
||||
let mut promise = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_named_property(self.env, self.value, c"ready".as_ptr().cast(), &mut promise)
|
||||
},
|
||||
"Get ready property failed"
|
||||
)?;
|
||||
Ok(PromiseRaw::new(self.env, promise))
|
||||
}
|
||||
|
||||
/// The `abort()` method of the `WritableStream` interface aborts the stream,
|
||||
/// signaling that the producer can no longer successfully write to the stream and it is to be immediately moved to an error state,
|
||||
/// with any queued writes discarded.
|
||||
pub fn abort(&mut self, reason: String) -> Result<PromiseRaw<()>> {
|
||||
let mut abort_fn = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_named_property(
|
||||
self.env,
|
||||
self.value,
|
||||
c"abort".as_ptr().cast(),
|
||||
&mut abort_fn,
|
||||
)
|
||||
},
|
||||
"Get abort property failed"
|
||||
)?;
|
||||
let reason_value = unsafe { ToNapiValue::to_napi_value(self.env, reason)? };
|
||||
let mut promise = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_call_function(
|
||||
self.env,
|
||||
self.value,
|
||||
abort_fn,
|
||||
1,
|
||||
[reason_value].as_ptr(),
|
||||
&mut promise,
|
||||
)
|
||||
},
|
||||
"Call abort function failed"
|
||||
)?;
|
||||
Ok(PromiseRaw::new(self.env, promise))
|
||||
}
|
||||
|
||||
/// The `close()` method of the `WritableStream` interface closes the associated stream.
|
||||
///
|
||||
/// All chunks written before this method is called are sent before the returned promise is fulfilled.
|
||||
pub fn close(&mut self) -> Result<PromiseRaw<()>> {
|
||||
let mut close_fn = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_get_named_property(
|
||||
self.env,
|
||||
self.value,
|
||||
c"close".as_ptr().cast(),
|
||||
&mut close_fn,
|
||||
)
|
||||
},
|
||||
"Get close property failed"
|
||||
)?;
|
||||
let mut promise = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe {
|
||||
sys::napi_call_function(
|
||||
self.env,
|
||||
self.value,
|
||||
close_fn,
|
||||
0,
|
||||
ptr::null_mut(),
|
||||
&mut promise,
|
||||
)
|
||||
},
|
||||
"Call close function failed"
|
||||
)?;
|
||||
Ok(PromiseRaw::new(self.env, promise))
|
||||
}
|
||||
}
|
||||
@ -11,6 +11,8 @@ pub use module_register::*;
|
||||
use super::sys;
|
||||
use crate::{JsError, Result, Status};
|
||||
|
||||
#[cfg(feature = "tokio_rt")]
|
||||
pub mod async_iterator;
|
||||
mod callback_info;
|
||||
mod env;
|
||||
mod error;
|
||||
|
||||
@ -333,7 +333,7 @@ impl Env {
|
||||
finalize_callback: Finalize,
|
||||
) -> Result<JsBufferValue>
|
||||
where
|
||||
Finalize: FnOnce(Hint, Env),
|
||||
Finalize: FnOnce(Env, Hint),
|
||||
{
|
||||
let mut raw_value = ptr::null_mut();
|
||||
if data.is_null() || data as *const u8 == EMPTY_VEC.as_ptr() {
|
||||
@ -363,7 +363,7 @@ impl Env {
|
||||
&mut raw_value,
|
||||
);
|
||||
data = result_data.cast();
|
||||
finalize(hint, *self);
|
||||
finalize(*self, hint);
|
||||
check_status!(status)?;
|
||||
} else {
|
||||
check_status!(status)?;
|
||||
@ -510,7 +510,7 @@ impl Env {
|
||||
finalize_callback: Finalize,
|
||||
) -> Result<JsArrayBufferValue>
|
||||
where
|
||||
Finalize: FnOnce(Hint, Env),
|
||||
Finalize: FnOnce(Env, Hint),
|
||||
{
|
||||
let mut raw_value = ptr::null_mut();
|
||||
let hint_ptr = Box::into_raw(Box::new((hint, finalize_callback)));
|
||||
@ -543,7 +543,7 @@ impl Env {
|
||||
let status =
|
||||
sys::napi_create_arraybuffer(self.0, length, &mut underlying_data, &mut raw_value);
|
||||
ptr::copy_nonoverlapping(data, underlying_data.cast(), length);
|
||||
finalize(hint, *self);
|
||||
finalize(*self, hint);
|
||||
check_status!(status)?;
|
||||
} else {
|
||||
check_status!(status)?;
|
||||
@ -1108,8 +1108,9 @@ impl Env {
|
||||
/// So you can access the `Env` and resolved value after the future completed
|
||||
pub fn spawn_future_with_callback<
|
||||
T: 'static + Send + ToNapiValue,
|
||||
V: ToNapiValue,
|
||||
F: 'static + Send + Future<Output = Result<T>>,
|
||||
R: 'static + FnOnce(&mut Env, &mut T) -> Result<()>,
|
||||
R: 'static + FnOnce(Env, T) -> Result<V>,
|
||||
>(
|
||||
&self,
|
||||
fut: F,
|
||||
@ -1117,8 +1118,8 @@ impl Env {
|
||||
) -> Result<PromiseRaw<T>> {
|
||||
use crate::tokio_runtime;
|
||||
|
||||
let promise = tokio_runtime::execute_tokio_future(self.0, fut, move |env, mut val| unsafe {
|
||||
callback(&mut Env::from_raw(env), &mut val)?;
|
||||
let promise = tokio_runtime::execute_tokio_future(self.0, fut, move |env, val| unsafe {
|
||||
let val = callback(Env::from_raw(env), val)?;
|
||||
ToNapiValue::to_napi_value(env, val)
|
||||
})?;
|
||||
|
||||
@ -1378,7 +1379,7 @@ impl Env {
|
||||
}
|
||||
|
||||
/// This function could be used for `BufferSlice::from_external` and want do noting when Buffer finalized.
|
||||
pub fn noop_finalize<Hint>(_hint: Hint, _env: Env) {}
|
||||
pub fn noop_finalize<Hint>(_env: Env, _hint: Hint) {}
|
||||
|
||||
unsafe extern "C" fn drop_buffer(
|
||||
_env: sys::napi_env,
|
||||
@ -1443,10 +1444,10 @@ pub(crate) unsafe extern "C" fn raw_finalize_with_custom_callback<Hint, Finalize
|
||||
_finalize_data: *mut c_void,
|
||||
finalize_hint: *mut c_void,
|
||||
) where
|
||||
Finalize: FnOnce(Hint, Env),
|
||||
Finalize: FnOnce(Env, Hint),
|
||||
{
|
||||
let (hint, callback) = unsafe { *Box::from_raw(finalize_hint as *mut (Hint, Finalize)) };
|
||||
callback(hint, Env::from_raw(env));
|
||||
callback(Env::from_raw(env), hint);
|
||||
}
|
||||
|
||||
#[cfg(feature = "napi8")]
|
||||
|
||||
@ -218,3 +218,8 @@ pub extern crate tokio;
|
||||
|
||||
#[cfg(feature = "error_anyhow")]
|
||||
pub extern crate anyhow;
|
||||
|
||||
#[cfg(feature = "web_stream")]
|
||||
pub extern crate futures_core;
|
||||
#[cfg(feature = "web_stream")]
|
||||
pub extern crate tokio_stream;
|
||||
|
||||
@ -232,18 +232,30 @@ pub fn execute_tokio_future<
|
||||
}
|
||||
|
||||
pub struct AsyncBlockBuilder<
|
||||
V: ToNapiValue + Send + 'static,
|
||||
V: Send + 'static,
|
||||
F: Future<Output = Result<V>> + Send + 'static,
|
||||
Dispose: FnOnce(Env) + 'static,
|
||||
Dispose: FnOnce(Env) -> Result<()> + 'static = fn(Env) -> Result<()>,
|
||||
> {
|
||||
inner: F,
|
||||
dispose: Option<Dispose>,
|
||||
}
|
||||
|
||||
impl<V: ToNapiValue + Send + 'static, F: Future<Output = Result<V>> + Send + 'static>
|
||||
AsyncBlockBuilder<V, F>
|
||||
{
|
||||
/// Create a new `AsyncBlockBuilder` with the given future, without dispose
|
||||
pub fn new(inner: F) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
dispose: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
V: ToNapiValue + Send + 'static,
|
||||
F: Future<Output = Result<V>> + Send + 'static,
|
||||
Dispose: FnOnce(Env),
|
||||
Dispose: FnOnce(Env) -> Result<()> + 'static,
|
||||
> AsyncBlockBuilder<V, F, Dispose>
|
||||
{
|
||||
pub fn with(inner: F) -> Self {
|
||||
@ -258,12 +270,12 @@ impl<
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self, env: Env) -> Result<AsyncBlock<V>> {
|
||||
pub fn build(self, env: &Env) -> Result<AsyncBlock<V>> {
|
||||
Ok(AsyncBlock {
|
||||
inner: execute_tokio_future(env.0, self.inner, |env, v| unsafe {
|
||||
if let Some(dispose) = self.dispose {
|
||||
let env = Env::from_raw(env);
|
||||
dispose(env);
|
||||
dispose(env)?;
|
||||
}
|
||||
V::to_napi_value(env, v)
|
||||
})?,
|
||||
@ -272,12 +284,29 @@ impl<
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AsyncBlock<T: ToNapiValue + Send + 'static> {
|
||||
impl<V: Send + 'static, F: Future<Output = Result<V>> + Send + 'static> AsyncBlockBuilder<V, F> {
|
||||
/// Create a new `AsyncBlockBuilder` with the given future, without dispose
|
||||
pub fn build_with_map<T: ToNapiValue, Map: FnOnce(Env, V) -> Result<T> + 'static>(
|
||||
env: &Env,
|
||||
inner: F,
|
||||
map: Map,
|
||||
) -> Result<AsyncBlock<T>> {
|
||||
Ok(AsyncBlock {
|
||||
inner: execute_tokio_future(env.0, inner, |env, v| unsafe {
|
||||
let v = map(Env::from_raw(env), v)?;
|
||||
T::to_napi_value(env, v)
|
||||
})?,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AsyncBlock<T: ToNapiValue + 'static> {
|
||||
inner: sys::napi_value,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: ToNapiValue + Send + 'static> ToNapiValue for AsyncBlock<T> {
|
||||
impl<T: ToNapiValue + 'static> ToNapiValue for AsyncBlock<T> {
|
||||
unsafe fn to_napi_value(_: napi_sys::napi_env, val: Self) -> Result<napi_sys::napi_value> {
|
||||
Ok(val.inner)
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ pub fn create_borrowed_buffer_with_finalize(env: Env) -> ContextlessResult<Buffe
|
||||
data_ptr,
|
||||
length,
|
||||
manually_drop,
|
||||
|mut hint: ManuallyDrop<Vec<u8>>, _| {
|
||||
|_, mut hint: ManuallyDrop<Vec<u8>>| {
|
||||
ManuallyDrop::drop(&mut hint);
|
||||
},
|
||||
)
|
||||
@ -78,7 +78,7 @@ pub fn create_empty_borrowed_buffer_with_finalize(
|
||||
data_ptr,
|
||||
length,
|
||||
manually_drop,
|
||||
|mut hint: ManuallyDrop<Vec<u8>>, _| {
|
||||
|_, mut hint: ManuallyDrop<Vec<u8>>| {
|
||||
ManuallyDrop::drop(&mut hint);
|
||||
},
|
||||
)
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
use std::convert::TryInto;
|
||||
|
||||
use napi::{
|
||||
bindgen_prelude::{Buffer, PromiseRaw},
|
||||
CallContext, Env, Error, JsNumber, JsObject, Result, Task,
|
||||
bindgen_prelude::Buffer, CallContext, Env, Error, JsNumber, JsObject, JsUnknown, Result, Task,
|
||||
};
|
||||
|
||||
struct ComputeFib {
|
||||
@ -36,11 +35,11 @@ fn fibonacci_native(n: u32) -> u32 {
|
||||
}
|
||||
|
||||
#[js_function(1)]
|
||||
fn test_spawn_thread(ctx: CallContext) -> Result<PromiseRaw<JsNumber>> {
|
||||
fn test_spawn_thread(ctx: CallContext) -> Result<JsUnknown> {
|
||||
let n = ctx.get::<JsNumber>(0)?;
|
||||
let task = ComputeFib::new(n.try_into()?);
|
||||
let async_promise = ctx.env.spawn(task)?;
|
||||
Ok(async_promise.promise_object())
|
||||
Ok(async_promise.promise_object().into_unknown())
|
||||
}
|
||||
|
||||
struct CountBufferLength {
|
||||
@ -74,11 +73,11 @@ impl Task for CountBufferLength {
|
||||
}
|
||||
|
||||
#[js_function(1)]
|
||||
fn test_spawn_thread_with_ref(ctx: CallContext) -> Result<PromiseRaw<JsNumber>> {
|
||||
fn test_spawn_thread_with_ref(ctx: CallContext) -> Result<JsUnknown> {
|
||||
let n = ctx.get::<Buffer>(0)?;
|
||||
let task = CountBufferLength::new(n);
|
||||
let async_work_promise = ctx.env.spawn(task)?;
|
||||
Ok(async_work_promise.promise_object())
|
||||
Ok(async_work_promise.promise_object().into_unknown())
|
||||
}
|
||||
|
||||
pub fn register_js(exports: &mut JsObject) -> Result<()> {
|
||||
|
||||
@ -16,6 +16,7 @@ error_try_builds = []
|
||||
[dependencies]
|
||||
chrono = "0.4"
|
||||
futures = "0.3"
|
||||
bytes = "1"
|
||||
napi-derive = { path = "../../crates/macro", features = ["type-def"] }
|
||||
napi-shared = { path = "../napi-shared" }
|
||||
serde = "1"
|
||||
@ -24,6 +25,8 @@ serde_derive = "1"
|
||||
serde_json = "1"
|
||||
indexmap = "2"
|
||||
rustc-hash = "2"
|
||||
tokio-stream = "0.1"
|
||||
tokio-util = { version = "0.7", features = ["io"] }
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
||||
napi = { path = "../../crates/napi", default-features = false, features = [
|
||||
@ -38,8 +41,10 @@ napi = { path = "../../crates/napi", default-features = false, features = [
|
||||
"tokio_rt",
|
||||
"tokio_fs",
|
||||
"tokio_macros",
|
||||
"tokio_io_util",
|
||||
"deferred_trace",
|
||||
"node_version_detect",
|
||||
"web_stream",
|
||||
] }
|
||||
tokio = { version = "1", features = ["rt", "time"] }
|
||||
|
||||
@ -56,7 +61,9 @@ napi = { path = "../../crates/napi", default-features = false, features = [
|
||||
"tokio_rt",
|
||||
"tokio_macros",
|
||||
"tokio_sync",
|
||||
"tokio_io_util",
|
||||
"deferred_trace",
|
||||
"web_stream",
|
||||
] }
|
||||
tokio = { version = "1", default-features = false, features = ["rt", "time"] }
|
||||
|
||||
|
||||
@ -268,6 +268,8 @@ Generated by [AVA](https://avajs.dev).
|
||||
␊
|
||||
export declare function acceptSlice(fixture: Uint8Array): bigint␊
|
||||
␊
|
||||
export declare function acceptStream(stream: ReadableStream<Uint8Array>): Promise<Buffer>␊
|
||||
␊
|
||||
export declare function acceptThreadsafeFunction(func: ((err: Error | null, arg: number) => any)): void␊
|
||||
␊
|
||||
export declare function acceptThreadsafeFunctionFatal(func: ((arg: number) => void)): void␊
|
||||
@ -431,6 +433,8 @@ Generated by [AVA](https://avajs.dev).
|
||||
␊
|
||||
export declare function createOptionalExternal(size?: number | undefined | null): ExternalObject<number> | null␊
|
||||
␊
|
||||
export declare function createReadableStream(): ReadableStream<Buffer>␊
|
||||
␊
|
||||
export declare function createReferenceOnFunction(cb: () => void): Promise<void>␊
|
||||
␊
|
||||
export declare function createSymbol(): symbol␊
|
||||
|
||||
Binary file not shown.
@ -2,6 +2,9 @@ import { Buffer } from 'node:buffer'
|
||||
import { exec } from 'node:child_process'
|
||||
import { join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { createReadStream } from 'node:fs'
|
||||
import { readFile as nodeReadFile } from 'node:fs/promises'
|
||||
import { Readable } from 'node:stream'
|
||||
|
||||
import { Subject, take } from 'rxjs'
|
||||
import Sinon, { spy } from 'sinon'
|
||||
@ -215,6 +218,8 @@ import {
|
||||
passSetWithHasherToJs,
|
||||
Rule,
|
||||
callRuleHandler,
|
||||
acceptStream,
|
||||
createReadableStream,
|
||||
} from '../index.cjs'
|
||||
|
||||
import { test } from './test.framework.js'
|
||||
@ -259,7 +264,7 @@ test('string', (t) => {
|
||||
test('array', (t) => {
|
||||
t.deepEqual(getNums(), [1, 1, 2, 3, 5, 8])
|
||||
t.deepEqual(getWords(), ['foo', 'bar'])
|
||||
t.deepEqual(getTuple([1, "test", 2]), 3)
|
||||
t.deepEqual(getTuple([1, 'test', 2]), 3)
|
||||
|
||||
t.is(sumNums([1, 2, 3, 4, 5]), 15)
|
||||
t.deepEqual(getNumArr(), [1, 2])
|
||||
@ -1490,3 +1495,28 @@ test('type', (t) => {
|
||||
}
|
||||
t.is(callRuleHandler(rule, 1), 6)
|
||||
})
|
||||
|
||||
test('acceptStream', async (t) => {
|
||||
if (process.version.startsWith('v18') || process.env.WASI_TEST) {
|
||||
// https://github.com/nodejs/node/issues/56432
|
||||
t.pass('Skip when Node.js is 18 and WASI due to bug')
|
||||
return
|
||||
}
|
||||
const selfPath = fileURLToPath(import.meta.url)
|
||||
const nodeFileStream = createReadStream(selfPath)
|
||||
const buffer = await acceptStream(Readable.toWeb(nodeFileStream))
|
||||
t.is(buffer.toString('utf-8'), await nodeReadFile(selfPath, 'utf-8'))
|
||||
})
|
||||
|
||||
test('create readable stream from channel', async (t) => {
|
||||
if (process.env.WASI_TEST) {
|
||||
t.pass('Skip when WASI due to bug')
|
||||
return
|
||||
}
|
||||
const stream = await createReadableStream()
|
||||
const chunks = []
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
}
|
||||
t.is(Buffer.concat(chunks).toString('utf-8'), 'hello'.repeat(100))
|
||||
})
|
||||
|
||||
@ -324,86 +324,88 @@ function __napi_rs_initialize_modules(__napiInstance) {
|
||||
__napiInstance.exports['__napi_register__btree_set_to_rust_328']?.()
|
||||
__napiInstance.exports['__napi_register__btree_set_to_js_329']?.()
|
||||
__napiInstance.exports['__napi_register__return_from_shared_crate_330']?.()
|
||||
__napiInstance.exports['__napi_register__contains_331']?.()
|
||||
__napiInstance.exports['__napi_register__concat_str_332']?.()
|
||||
__napiInstance.exports['__napi_register__concat_utf16_333']?.()
|
||||
__napiInstance.exports['__napi_register__concat_latin1_334']?.()
|
||||
__napiInstance.exports['__napi_register__roundtrip_str_335']?.()
|
||||
__napiInstance.exports['__napi_register__return_c_string_336']?.()
|
||||
__napiInstance.exports['__napi_register__set_symbol_in_obj_337']?.()
|
||||
__napiInstance.exports['__napi_register__create_symbol_338']?.()
|
||||
__napiInstance.exports['__napi_register__create_symbol_for_339']?.()
|
||||
__napiInstance.exports['__napi_register__DelaySum_impl_340']?.()
|
||||
__napiInstance.exports['__napi_register__without_abort_controller_341']?.()
|
||||
__napiInstance.exports['__napi_register__with_abort_controller_342']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncTaskVoidReturn_impl_343']?.()
|
||||
__napiInstance.exports['__napi_register__async_task_void_return_344']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncTaskOptionalReturn_impl_345']?.()
|
||||
__napiInstance.exports['__napi_register__async_task_optional_return_346']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncTaskReadFile_impl_347']?.()
|
||||
__napiInstance.exports['__napi_register__async_task_read_file_348']?.()
|
||||
__napiInstance.exports['__napi_register__call_threadsafe_function_349']?.()
|
||||
__napiInstance.exports['__napi_register__call_long_threadsafe_function_350']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_throw_error_351']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_fatal_mode_352']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_fatal_mode_error_353']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_closure_capture_354']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_call_with_callback_355']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_async_call_356']?.()
|
||||
__napiInstance.exports['__napi_register__accept_threadsafe_function_357']?.()
|
||||
__napiInstance.exports['__napi_register__accept_threadsafe_function_fatal_358']?.()
|
||||
__napiInstance.exports['__napi_register__accept_threadsafe_function_tuple_args_359']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_return_promise_360']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_return_promise_timeout_361']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_throw_from_js_362']?.()
|
||||
__napiInstance.exports['__napi_register__spawn_thread_in_thread_363']?.()
|
||||
__napiInstance.exports['__napi_register__Pet_struct_364']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_in_either_365']?.()
|
||||
__napiInstance.exports['__napi_register__MyVec_struct_366']?.()
|
||||
__napiInstance.exports['__napi_register__get_my_vec_367']?.()
|
||||
__napiInstance.exports['__napi_register__CustomU32_368']?.()
|
||||
__napiInstance.exports['__napi_register__MyPromise_369']?.()
|
||||
__napiInstance.exports['__napi_register__Nullable_370']?.()
|
||||
__napiInstance.exports['__napi_register__VoidNullable_371']?.()
|
||||
__napiInstance.exports['__napi_register__RuleHandler_372']?.()
|
||||
__napiInstance.exports['__napi_register__Rule_struct_373']?.()
|
||||
__napiInstance.exports['__napi_register__call_rule_handler_374']?.()
|
||||
__napiInstance.exports['__napi_register__get_buffer_375']?.()
|
||||
__napiInstance.exports['__napi_register__get_buffer_slice_376']?.()
|
||||
__napiInstance.exports['__napi_register__append_buffer_377']?.()
|
||||
__napiInstance.exports['__napi_register__get_empty_buffer_378']?.()
|
||||
__napiInstance.exports['__napi_register__create_external_buffer_slice_379']?.()
|
||||
__napiInstance.exports['__napi_register__create_buffer_slice_from_copied_data_380']?.()
|
||||
__napiInstance.exports['__napi_register__get_empty_typed_array_381']?.()
|
||||
__napiInstance.exports['__napi_register__convert_u32_array_382']?.()
|
||||
__napiInstance.exports['__napi_register__create_external_typed_array_383']?.()
|
||||
__napiInstance.exports['__napi_register__mutate_typed_array_384']?.()
|
||||
__napiInstance.exports['__napi_register__deref_uint8_array_385']?.()
|
||||
__napiInstance.exports['__napi_register__buffer_pass_through_386']?.()
|
||||
__napiInstance.exports['__napi_register__buffer_with_async_block_387']?.()
|
||||
__napiInstance.exports['__napi_register__array_buffer_pass_through_388']?.()
|
||||
__napiInstance.exports['__napi_register__accept_slice_389']?.()
|
||||
__napiInstance.exports['__napi_register__accept_arraybuffer_390']?.()
|
||||
__napiInstance.exports['__napi_register__create_arraybuffer_391']?.()
|
||||
__napiInstance.exports['__napi_register__u8_array_to_array_392']?.()
|
||||
__napiInstance.exports['__napi_register__i8_array_to_array_393']?.()
|
||||
__napiInstance.exports['__napi_register__u16_array_to_array_394']?.()
|
||||
__napiInstance.exports['__napi_register__i16_array_to_array_395']?.()
|
||||
__napiInstance.exports['__napi_register__u32_array_to_array_396']?.()
|
||||
__napiInstance.exports['__napi_register__i32_array_to_array_397']?.()
|
||||
__napiInstance.exports['__napi_register__f32_array_to_array_398']?.()
|
||||
__napiInstance.exports['__napi_register__f64_array_to_array_399']?.()
|
||||
__napiInstance.exports['__napi_register__u64_array_to_array_400']?.()
|
||||
__napiInstance.exports['__napi_register__i64_array_to_array_401']?.()
|
||||
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_402']?.()
|
||||
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_and_buffer_slice_403']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncBuffer_impl_404']?.()
|
||||
__napiInstance.exports['__napi_register__async_reduce_buffer_405']?.()
|
||||
__napiInstance.exports['__napi_register__async_buffer_to_array_406']?.()
|
||||
__napiInstance.exports['__napi_register__u_init8_array_from_string_407']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncReader_impl_408']?.()
|
||||
__napiInstance.exports['__napi_register__Reader_struct_409']?.()
|
||||
__napiInstance.exports['__napi_register__Reader_impl_411']?.()
|
||||
__napiInstance.exports['__napi_register__accept_stream_331']?.()
|
||||
__napiInstance.exports['__napi_register__create_readable_stream_332']?.()
|
||||
__napiInstance.exports['__napi_register__contains_333']?.()
|
||||
__napiInstance.exports['__napi_register__concat_str_334']?.()
|
||||
__napiInstance.exports['__napi_register__concat_utf16_335']?.()
|
||||
__napiInstance.exports['__napi_register__concat_latin1_336']?.()
|
||||
__napiInstance.exports['__napi_register__roundtrip_str_337']?.()
|
||||
__napiInstance.exports['__napi_register__return_c_string_338']?.()
|
||||
__napiInstance.exports['__napi_register__set_symbol_in_obj_339']?.()
|
||||
__napiInstance.exports['__napi_register__create_symbol_340']?.()
|
||||
__napiInstance.exports['__napi_register__create_symbol_for_341']?.()
|
||||
__napiInstance.exports['__napi_register__DelaySum_impl_342']?.()
|
||||
__napiInstance.exports['__napi_register__without_abort_controller_343']?.()
|
||||
__napiInstance.exports['__napi_register__with_abort_controller_344']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncTaskVoidReturn_impl_345']?.()
|
||||
__napiInstance.exports['__napi_register__async_task_void_return_346']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncTaskOptionalReturn_impl_347']?.()
|
||||
__napiInstance.exports['__napi_register__async_task_optional_return_348']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncTaskReadFile_impl_349']?.()
|
||||
__napiInstance.exports['__napi_register__async_task_read_file_350']?.()
|
||||
__napiInstance.exports['__napi_register__call_threadsafe_function_351']?.()
|
||||
__napiInstance.exports['__napi_register__call_long_threadsafe_function_352']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_throw_error_353']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_fatal_mode_354']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_fatal_mode_error_355']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_closure_capture_356']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_call_with_callback_357']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_async_call_358']?.()
|
||||
__napiInstance.exports['__napi_register__accept_threadsafe_function_359']?.()
|
||||
__napiInstance.exports['__napi_register__accept_threadsafe_function_fatal_360']?.()
|
||||
__napiInstance.exports['__napi_register__accept_threadsafe_function_tuple_args_361']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_return_promise_362']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_return_promise_timeout_363']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_throw_from_js_364']?.()
|
||||
__napiInstance.exports['__napi_register__spawn_thread_in_thread_365']?.()
|
||||
__napiInstance.exports['__napi_register__Pet_struct_366']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_in_either_367']?.()
|
||||
__napiInstance.exports['__napi_register__MyVec_struct_368']?.()
|
||||
__napiInstance.exports['__napi_register__get_my_vec_369']?.()
|
||||
__napiInstance.exports['__napi_register__CustomU32_370']?.()
|
||||
__napiInstance.exports['__napi_register__MyPromise_371']?.()
|
||||
__napiInstance.exports['__napi_register__Nullable_372']?.()
|
||||
__napiInstance.exports['__napi_register__VoidNullable_373']?.()
|
||||
__napiInstance.exports['__napi_register__RuleHandler_374']?.()
|
||||
__napiInstance.exports['__napi_register__Rule_struct_375']?.()
|
||||
__napiInstance.exports['__napi_register__call_rule_handler_376']?.()
|
||||
__napiInstance.exports['__napi_register__get_buffer_377']?.()
|
||||
__napiInstance.exports['__napi_register__get_buffer_slice_378']?.()
|
||||
__napiInstance.exports['__napi_register__append_buffer_379']?.()
|
||||
__napiInstance.exports['__napi_register__get_empty_buffer_380']?.()
|
||||
__napiInstance.exports['__napi_register__create_external_buffer_slice_381']?.()
|
||||
__napiInstance.exports['__napi_register__create_buffer_slice_from_copied_data_382']?.()
|
||||
__napiInstance.exports['__napi_register__get_empty_typed_array_383']?.()
|
||||
__napiInstance.exports['__napi_register__convert_u32_array_384']?.()
|
||||
__napiInstance.exports['__napi_register__create_external_typed_array_385']?.()
|
||||
__napiInstance.exports['__napi_register__mutate_typed_array_386']?.()
|
||||
__napiInstance.exports['__napi_register__deref_uint8_array_387']?.()
|
||||
__napiInstance.exports['__napi_register__buffer_pass_through_388']?.()
|
||||
__napiInstance.exports['__napi_register__buffer_with_async_block_389']?.()
|
||||
__napiInstance.exports['__napi_register__array_buffer_pass_through_390']?.()
|
||||
__napiInstance.exports['__napi_register__accept_slice_391']?.()
|
||||
__napiInstance.exports['__napi_register__accept_arraybuffer_392']?.()
|
||||
__napiInstance.exports['__napi_register__create_arraybuffer_393']?.()
|
||||
__napiInstance.exports['__napi_register__u8_array_to_array_394']?.()
|
||||
__napiInstance.exports['__napi_register__i8_array_to_array_395']?.()
|
||||
__napiInstance.exports['__napi_register__u16_array_to_array_396']?.()
|
||||
__napiInstance.exports['__napi_register__i16_array_to_array_397']?.()
|
||||
__napiInstance.exports['__napi_register__u32_array_to_array_398']?.()
|
||||
__napiInstance.exports['__napi_register__i32_array_to_array_399']?.()
|
||||
__napiInstance.exports['__napi_register__f32_array_to_array_400']?.()
|
||||
__napiInstance.exports['__napi_register__f64_array_to_array_401']?.()
|
||||
__napiInstance.exports['__napi_register__u64_array_to_array_402']?.()
|
||||
__napiInstance.exports['__napi_register__i64_array_to_array_403']?.()
|
||||
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_404']?.()
|
||||
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_and_buffer_slice_405']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncBuffer_impl_406']?.()
|
||||
__napiInstance.exports['__napi_register__async_reduce_buffer_407']?.()
|
||||
__napiInstance.exports['__napi_register__async_buffer_to_array_408']?.()
|
||||
__napiInstance.exports['__napi_register__u_init8_array_from_string_409']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncReader_impl_410']?.()
|
||||
__napiInstance.exports['__napi_register__Reader_struct_411']?.()
|
||||
__napiInstance.exports['__napi_register__Reader_impl_413']?.()
|
||||
}
|
||||
export const Animal = __napiModule.exports.Animal
|
||||
export const AnimalWithDefaultConstructor = __napiModule.exports.AnimalWithDefaultConstructor
|
||||
@ -449,6 +451,7 @@ export const UseNullableClass = __napiModule.exports.UseNullableClass
|
||||
export const Width = __napiModule.exports.Width
|
||||
export const acceptArraybuffer = __napiModule.exports.acceptArraybuffer
|
||||
export const acceptSlice = __napiModule.exports.acceptSlice
|
||||
export const acceptStream = __napiModule.exports.acceptStream
|
||||
export const acceptThreadsafeFunction = __napiModule.exports.acceptThreadsafeFunction
|
||||
export const acceptThreadsafeFunctionFatal = __napiModule.exports.acceptThreadsafeFunctionFatal
|
||||
export const acceptThreadsafeFunctionTupleArgs = __napiModule.exports.acceptThreadsafeFunctionTupleArgs
|
||||
@ -521,6 +524,7 @@ export const createObj = __napiModule.exports.createObj
|
||||
export const createObjectWithClassField = __napiModule.exports.createObjectWithClassField
|
||||
export const createObjWithProperty = __napiModule.exports.createObjWithProperty
|
||||
export const createOptionalExternal = __napiModule.exports.createOptionalExternal
|
||||
export const createReadableStream = __napiModule.exports.createReadableStream
|
||||
export const createReferenceOnFunction = __napiModule.exports.createReferenceOnFunction
|
||||
export const createSymbol = __napiModule.exports.createSymbol
|
||||
export const createSymbolFor = __napiModule.exports.createSymbolFor
|
||||
|
||||
@ -348,86 +348,88 @@ function __napi_rs_initialize_modules(__napiInstance) {
|
||||
__napiInstance.exports['__napi_register__btree_set_to_rust_328']?.()
|
||||
__napiInstance.exports['__napi_register__btree_set_to_js_329']?.()
|
||||
__napiInstance.exports['__napi_register__return_from_shared_crate_330']?.()
|
||||
__napiInstance.exports['__napi_register__contains_331']?.()
|
||||
__napiInstance.exports['__napi_register__concat_str_332']?.()
|
||||
__napiInstance.exports['__napi_register__concat_utf16_333']?.()
|
||||
__napiInstance.exports['__napi_register__concat_latin1_334']?.()
|
||||
__napiInstance.exports['__napi_register__roundtrip_str_335']?.()
|
||||
__napiInstance.exports['__napi_register__return_c_string_336']?.()
|
||||
__napiInstance.exports['__napi_register__set_symbol_in_obj_337']?.()
|
||||
__napiInstance.exports['__napi_register__create_symbol_338']?.()
|
||||
__napiInstance.exports['__napi_register__create_symbol_for_339']?.()
|
||||
__napiInstance.exports['__napi_register__DelaySum_impl_340']?.()
|
||||
__napiInstance.exports['__napi_register__without_abort_controller_341']?.()
|
||||
__napiInstance.exports['__napi_register__with_abort_controller_342']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncTaskVoidReturn_impl_343']?.()
|
||||
__napiInstance.exports['__napi_register__async_task_void_return_344']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncTaskOptionalReturn_impl_345']?.()
|
||||
__napiInstance.exports['__napi_register__async_task_optional_return_346']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncTaskReadFile_impl_347']?.()
|
||||
__napiInstance.exports['__napi_register__async_task_read_file_348']?.()
|
||||
__napiInstance.exports['__napi_register__call_threadsafe_function_349']?.()
|
||||
__napiInstance.exports['__napi_register__call_long_threadsafe_function_350']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_throw_error_351']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_fatal_mode_352']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_fatal_mode_error_353']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_closure_capture_354']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_call_with_callback_355']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_async_call_356']?.()
|
||||
__napiInstance.exports['__napi_register__accept_threadsafe_function_357']?.()
|
||||
__napiInstance.exports['__napi_register__accept_threadsafe_function_fatal_358']?.()
|
||||
__napiInstance.exports['__napi_register__accept_threadsafe_function_tuple_args_359']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_return_promise_360']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_return_promise_timeout_361']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_throw_from_js_362']?.()
|
||||
__napiInstance.exports['__napi_register__spawn_thread_in_thread_363']?.()
|
||||
__napiInstance.exports['__napi_register__Pet_struct_364']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_in_either_365']?.()
|
||||
__napiInstance.exports['__napi_register__MyVec_struct_366']?.()
|
||||
__napiInstance.exports['__napi_register__get_my_vec_367']?.()
|
||||
__napiInstance.exports['__napi_register__CustomU32_368']?.()
|
||||
__napiInstance.exports['__napi_register__MyPromise_369']?.()
|
||||
__napiInstance.exports['__napi_register__Nullable_370']?.()
|
||||
__napiInstance.exports['__napi_register__VoidNullable_371']?.()
|
||||
__napiInstance.exports['__napi_register__RuleHandler_372']?.()
|
||||
__napiInstance.exports['__napi_register__Rule_struct_373']?.()
|
||||
__napiInstance.exports['__napi_register__call_rule_handler_374']?.()
|
||||
__napiInstance.exports['__napi_register__get_buffer_375']?.()
|
||||
__napiInstance.exports['__napi_register__get_buffer_slice_376']?.()
|
||||
__napiInstance.exports['__napi_register__append_buffer_377']?.()
|
||||
__napiInstance.exports['__napi_register__get_empty_buffer_378']?.()
|
||||
__napiInstance.exports['__napi_register__create_external_buffer_slice_379']?.()
|
||||
__napiInstance.exports['__napi_register__create_buffer_slice_from_copied_data_380']?.()
|
||||
__napiInstance.exports['__napi_register__get_empty_typed_array_381']?.()
|
||||
__napiInstance.exports['__napi_register__convert_u32_array_382']?.()
|
||||
__napiInstance.exports['__napi_register__create_external_typed_array_383']?.()
|
||||
__napiInstance.exports['__napi_register__mutate_typed_array_384']?.()
|
||||
__napiInstance.exports['__napi_register__deref_uint8_array_385']?.()
|
||||
__napiInstance.exports['__napi_register__buffer_pass_through_386']?.()
|
||||
__napiInstance.exports['__napi_register__buffer_with_async_block_387']?.()
|
||||
__napiInstance.exports['__napi_register__array_buffer_pass_through_388']?.()
|
||||
__napiInstance.exports['__napi_register__accept_slice_389']?.()
|
||||
__napiInstance.exports['__napi_register__accept_arraybuffer_390']?.()
|
||||
__napiInstance.exports['__napi_register__create_arraybuffer_391']?.()
|
||||
__napiInstance.exports['__napi_register__u8_array_to_array_392']?.()
|
||||
__napiInstance.exports['__napi_register__i8_array_to_array_393']?.()
|
||||
__napiInstance.exports['__napi_register__u16_array_to_array_394']?.()
|
||||
__napiInstance.exports['__napi_register__i16_array_to_array_395']?.()
|
||||
__napiInstance.exports['__napi_register__u32_array_to_array_396']?.()
|
||||
__napiInstance.exports['__napi_register__i32_array_to_array_397']?.()
|
||||
__napiInstance.exports['__napi_register__f32_array_to_array_398']?.()
|
||||
__napiInstance.exports['__napi_register__f64_array_to_array_399']?.()
|
||||
__napiInstance.exports['__napi_register__u64_array_to_array_400']?.()
|
||||
__napiInstance.exports['__napi_register__i64_array_to_array_401']?.()
|
||||
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_402']?.()
|
||||
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_and_buffer_slice_403']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncBuffer_impl_404']?.()
|
||||
__napiInstance.exports['__napi_register__async_reduce_buffer_405']?.()
|
||||
__napiInstance.exports['__napi_register__async_buffer_to_array_406']?.()
|
||||
__napiInstance.exports['__napi_register__u_init8_array_from_string_407']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncReader_impl_408']?.()
|
||||
__napiInstance.exports['__napi_register__Reader_struct_409']?.()
|
||||
__napiInstance.exports['__napi_register__Reader_impl_411']?.()
|
||||
__napiInstance.exports['__napi_register__accept_stream_331']?.()
|
||||
__napiInstance.exports['__napi_register__create_readable_stream_332']?.()
|
||||
__napiInstance.exports['__napi_register__contains_333']?.()
|
||||
__napiInstance.exports['__napi_register__concat_str_334']?.()
|
||||
__napiInstance.exports['__napi_register__concat_utf16_335']?.()
|
||||
__napiInstance.exports['__napi_register__concat_latin1_336']?.()
|
||||
__napiInstance.exports['__napi_register__roundtrip_str_337']?.()
|
||||
__napiInstance.exports['__napi_register__return_c_string_338']?.()
|
||||
__napiInstance.exports['__napi_register__set_symbol_in_obj_339']?.()
|
||||
__napiInstance.exports['__napi_register__create_symbol_340']?.()
|
||||
__napiInstance.exports['__napi_register__create_symbol_for_341']?.()
|
||||
__napiInstance.exports['__napi_register__DelaySum_impl_342']?.()
|
||||
__napiInstance.exports['__napi_register__without_abort_controller_343']?.()
|
||||
__napiInstance.exports['__napi_register__with_abort_controller_344']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncTaskVoidReturn_impl_345']?.()
|
||||
__napiInstance.exports['__napi_register__async_task_void_return_346']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncTaskOptionalReturn_impl_347']?.()
|
||||
__napiInstance.exports['__napi_register__async_task_optional_return_348']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncTaskReadFile_impl_349']?.()
|
||||
__napiInstance.exports['__napi_register__async_task_read_file_350']?.()
|
||||
__napiInstance.exports['__napi_register__call_threadsafe_function_351']?.()
|
||||
__napiInstance.exports['__napi_register__call_long_threadsafe_function_352']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_throw_error_353']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_fatal_mode_354']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_fatal_mode_error_355']?.()
|
||||
__napiInstance.exports['__napi_register__threadsafe_function_closure_capture_356']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_call_with_callback_357']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_async_call_358']?.()
|
||||
__napiInstance.exports['__napi_register__accept_threadsafe_function_359']?.()
|
||||
__napiInstance.exports['__napi_register__accept_threadsafe_function_fatal_360']?.()
|
||||
__napiInstance.exports['__napi_register__accept_threadsafe_function_tuple_args_361']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_return_promise_362']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_return_promise_timeout_363']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_throw_from_js_364']?.()
|
||||
__napiInstance.exports['__napi_register__spawn_thread_in_thread_365']?.()
|
||||
__napiInstance.exports['__napi_register__Pet_struct_366']?.()
|
||||
__napiInstance.exports['__napi_register__tsfn_in_either_367']?.()
|
||||
__napiInstance.exports['__napi_register__MyVec_struct_368']?.()
|
||||
__napiInstance.exports['__napi_register__get_my_vec_369']?.()
|
||||
__napiInstance.exports['__napi_register__CustomU32_370']?.()
|
||||
__napiInstance.exports['__napi_register__MyPromise_371']?.()
|
||||
__napiInstance.exports['__napi_register__Nullable_372']?.()
|
||||
__napiInstance.exports['__napi_register__VoidNullable_373']?.()
|
||||
__napiInstance.exports['__napi_register__RuleHandler_374']?.()
|
||||
__napiInstance.exports['__napi_register__Rule_struct_375']?.()
|
||||
__napiInstance.exports['__napi_register__call_rule_handler_376']?.()
|
||||
__napiInstance.exports['__napi_register__get_buffer_377']?.()
|
||||
__napiInstance.exports['__napi_register__get_buffer_slice_378']?.()
|
||||
__napiInstance.exports['__napi_register__append_buffer_379']?.()
|
||||
__napiInstance.exports['__napi_register__get_empty_buffer_380']?.()
|
||||
__napiInstance.exports['__napi_register__create_external_buffer_slice_381']?.()
|
||||
__napiInstance.exports['__napi_register__create_buffer_slice_from_copied_data_382']?.()
|
||||
__napiInstance.exports['__napi_register__get_empty_typed_array_383']?.()
|
||||
__napiInstance.exports['__napi_register__convert_u32_array_384']?.()
|
||||
__napiInstance.exports['__napi_register__create_external_typed_array_385']?.()
|
||||
__napiInstance.exports['__napi_register__mutate_typed_array_386']?.()
|
||||
__napiInstance.exports['__napi_register__deref_uint8_array_387']?.()
|
||||
__napiInstance.exports['__napi_register__buffer_pass_through_388']?.()
|
||||
__napiInstance.exports['__napi_register__buffer_with_async_block_389']?.()
|
||||
__napiInstance.exports['__napi_register__array_buffer_pass_through_390']?.()
|
||||
__napiInstance.exports['__napi_register__accept_slice_391']?.()
|
||||
__napiInstance.exports['__napi_register__accept_arraybuffer_392']?.()
|
||||
__napiInstance.exports['__napi_register__create_arraybuffer_393']?.()
|
||||
__napiInstance.exports['__napi_register__u8_array_to_array_394']?.()
|
||||
__napiInstance.exports['__napi_register__i8_array_to_array_395']?.()
|
||||
__napiInstance.exports['__napi_register__u16_array_to_array_396']?.()
|
||||
__napiInstance.exports['__napi_register__i16_array_to_array_397']?.()
|
||||
__napiInstance.exports['__napi_register__u32_array_to_array_398']?.()
|
||||
__napiInstance.exports['__napi_register__i32_array_to_array_399']?.()
|
||||
__napiInstance.exports['__napi_register__f32_array_to_array_400']?.()
|
||||
__napiInstance.exports['__napi_register__f64_array_to_array_401']?.()
|
||||
__napiInstance.exports['__napi_register__u64_array_to_array_402']?.()
|
||||
__napiInstance.exports['__napi_register__i64_array_to_array_403']?.()
|
||||
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_404']?.()
|
||||
__napiInstance.exports['__napi_register__accept_uint8_clamped_slice_and_buffer_slice_405']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncBuffer_impl_406']?.()
|
||||
__napiInstance.exports['__napi_register__async_reduce_buffer_407']?.()
|
||||
__napiInstance.exports['__napi_register__async_buffer_to_array_408']?.()
|
||||
__napiInstance.exports['__napi_register__u_init8_array_from_string_409']?.()
|
||||
__napiInstance.exports['__napi_register__AsyncReader_impl_410']?.()
|
||||
__napiInstance.exports['__napi_register__Reader_struct_411']?.()
|
||||
__napiInstance.exports['__napi_register__Reader_impl_413']?.()
|
||||
}
|
||||
module.exports.Animal = __napiModule.exports.Animal
|
||||
module.exports.AnimalWithDefaultConstructor = __napiModule.exports.AnimalWithDefaultConstructor
|
||||
@ -473,6 +475,7 @@ module.exports.UseNullableClass = __napiModule.exports.UseNullableClass
|
||||
module.exports.Width = __napiModule.exports.Width
|
||||
module.exports.acceptArraybuffer = __napiModule.exports.acceptArraybuffer
|
||||
module.exports.acceptSlice = __napiModule.exports.acceptSlice
|
||||
module.exports.acceptStream = __napiModule.exports.acceptStream
|
||||
module.exports.acceptThreadsafeFunction = __napiModule.exports.acceptThreadsafeFunction
|
||||
module.exports.acceptThreadsafeFunctionFatal = __napiModule.exports.acceptThreadsafeFunctionFatal
|
||||
module.exports.acceptThreadsafeFunctionTupleArgs = __napiModule.exports.acceptThreadsafeFunctionTupleArgs
|
||||
@ -545,6 +548,7 @@ module.exports.createObj = __napiModule.exports.createObj
|
||||
module.exports.createObjectWithClassField = __napiModule.exports.createObjectWithClassField
|
||||
module.exports.createObjWithProperty = __napiModule.exports.createObjWithProperty
|
||||
module.exports.createOptionalExternal = __napiModule.exports.createOptionalExternal
|
||||
module.exports.createReadableStream = __napiModule.exports.createReadableStream
|
||||
module.exports.createReferenceOnFunction = __napiModule.exports.createReferenceOnFunction
|
||||
module.exports.createSymbol = __napiModule.exports.createSymbol
|
||||
module.exports.createSymbolFor = __napiModule.exports.createSymbolFor
|
||||
|
||||
@ -408,6 +408,7 @@ module.exports.UseNullableClass = nativeBinding.UseNullableClass
|
||||
module.exports.Width = nativeBinding.Width
|
||||
module.exports.acceptArraybuffer = nativeBinding.acceptArraybuffer
|
||||
module.exports.acceptSlice = nativeBinding.acceptSlice
|
||||
module.exports.acceptStream = nativeBinding.acceptStream
|
||||
module.exports.acceptThreadsafeFunction = nativeBinding.acceptThreadsafeFunction
|
||||
module.exports.acceptThreadsafeFunctionFatal = nativeBinding.acceptThreadsafeFunctionFatal
|
||||
module.exports.acceptThreadsafeFunctionTupleArgs = nativeBinding.acceptThreadsafeFunctionTupleArgs
|
||||
@ -480,6 +481,7 @@ module.exports.createObj = nativeBinding.createObj
|
||||
module.exports.createObjectWithClassField = nativeBinding.createObjectWithClassField
|
||||
module.exports.createObjWithProperty = nativeBinding.createObjWithProperty
|
||||
module.exports.createOptionalExternal = nativeBinding.createOptionalExternal
|
||||
module.exports.createReadableStream = nativeBinding.createReadableStream
|
||||
module.exports.createReferenceOnFunction = nativeBinding.createReferenceOnFunction
|
||||
module.exports.createSymbol = nativeBinding.createSymbol
|
||||
module.exports.createSymbolFor = nativeBinding.createSymbolFor
|
||||
|
||||
@ -258,6 +258,8 @@ export declare function acceptArraybuffer(fixture: ArrayBuffer): bigint
|
||||
|
||||
export declare function acceptSlice(fixture: Uint8Array): bigint
|
||||
|
||||
export declare function acceptStream(stream: ReadableStream<Uint8Array>): Promise<Buffer>
|
||||
|
||||
export declare function acceptThreadsafeFunction(func: ((err: Error | null, arg: number) => any)): void
|
||||
|
||||
export declare function acceptThreadsafeFunctionFatal(func: ((arg: number) => void)): void
|
||||
@ -421,6 +423,8 @@ export declare function createObjWithProperty(): { value: ArrayBuffer, get gette
|
||||
|
||||
export declare function createOptionalExternal(size?: number | undefined | null): ExternalObject<number> | null
|
||||
|
||||
export declare function createReadableStream(): ReadableStream<Buffer>
|
||||
|
||||
export declare function createReferenceOnFunction(cb: () => void): Promise<void>
|
||||
|
||||
export declare function createSymbol(): symbol
|
||||
|
||||
@ -76,7 +76,7 @@ fn callback_return_promise<T: Fn() -> Result<JsUnknown>>(
|
||||
|
||||
#[napi(ts_return_type = "Promise<string>")]
|
||||
pub fn callback_return_promise_and_spawn<F: Fn(String) -> Result<Promise<String>>>(
|
||||
env: Env,
|
||||
env: &Env,
|
||||
js_func: F,
|
||||
) -> napi::Result<PromiseRaw<String>> {
|
||||
let promise = js_func("Hello".to_owned())?;
|
||||
|
||||
@ -52,7 +52,10 @@ pub fn call_function_with_arg(
|
||||
}
|
||||
|
||||
#[napi(ts_return_type = "Promise<void>")]
|
||||
pub fn create_reference_on_function(env: Env, cb: Function<(), ()>) -> Result<PromiseRaw<()>> {
|
||||
pub fn create_reference_on_function<'env>(
|
||||
env: &'env Env,
|
||||
cb: Function<'env, (), ()>,
|
||||
) -> Result<PromiseRaw<'env, ()>> {
|
||||
let reference = cb.create_ref()?;
|
||||
env.spawn_future_with_callback(
|
||||
async {
|
||||
@ -60,7 +63,7 @@ pub fn create_reference_on_function(env: Env, cb: Function<(), ()>) -> Result<Pr
|
||||
Ok(())
|
||||
},
|
||||
move |env, _| {
|
||||
let cb = reference.borrow_back(env)?;
|
||||
let cb = reference.borrow_back(&env)?;
|
||||
cb.call(())?;
|
||||
Ok(())
|
||||
},
|
||||
|
||||
@ -64,6 +64,7 @@ mod reference;
|
||||
mod serde;
|
||||
mod set;
|
||||
mod shared;
|
||||
mod stream;
|
||||
mod string;
|
||||
mod symbol;
|
||||
mod task;
|
||||
|
||||
@ -7,12 +7,12 @@ pub async fn async_plus_100(p: Promise<u32>) -> Result<u32> {
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn call_then_on_promise(mut input: PromiseRaw<u32>) -> Result<PromiseRaw<String>> {
|
||||
pub fn call_then_on_promise(input: PromiseRaw<u32>) -> Result<PromiseRaw<String>> {
|
||||
input.then(|v| Ok(format!("{}", v.value)))
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn call_catch_on_promise(mut input: PromiseRaw<u32>) -> Result<PromiseRaw<String>> {
|
||||
pub fn call_catch_on_promise(input: PromiseRaw<'_, u32>) -> Result<PromiseRaw<'_, String>> {
|
||||
input.catch(|e: CallbackContext<String>| Ok(e.value))
|
||||
}
|
||||
|
||||
|
||||
58
examples/napi/src/stream.rs
Normal file
58
examples/napi/src/stream.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use bytes::BytesMut;
|
||||
use napi::bindgen_prelude::*;
|
||||
use tokio::sync::mpsc::error::TrySendError;
|
||||
use tokio_stream::{wrappers::ReceiverStream, StreamExt};
|
||||
use tokio_util::io::{read_buf, StreamReader};
|
||||
|
||||
#[napi]
|
||||
pub fn accept_stream(
|
||||
env: &Env,
|
||||
stream: ReadableStream<Uint8Array>,
|
||||
) -> Result<AsyncBlock<BufferSlice<'static>>> {
|
||||
let web_readable_stream = stream.read()?;
|
||||
let mut input = StreamReader::new(web_readable_stream.map(|chunk| {
|
||||
chunk
|
||||
.map(bytes::Bytes::from_owner)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.reason))
|
||||
}));
|
||||
AsyncBlockBuilder::build_with_map(
|
||||
env,
|
||||
async move {
|
||||
let mut bytes_mut = BytesMut::new();
|
||||
loop {
|
||||
let n = read_buf(&mut input, &mut bytes_mut).await?;
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(bytes_mut)
|
||||
},
|
||||
|env, mut value| {
|
||||
let value_ptr = value.as_mut_ptr();
|
||||
unsafe {
|
||||
BufferSlice::from_external(&env, value_ptr, value.len(), value, move |_, bytes| {
|
||||
drop(bytes);
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn create_readable_stream(env: &Env) -> Result<ReadableStream<BufferSlice>> {
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(100);
|
||||
std::thread::spawn(move || {
|
||||
for _ in 0..100 {
|
||||
match tx.try_send(Ok(b"hello".to_vec())) {
|
||||
Err(TrySendError::Closed(_)) => {
|
||||
panic!("closed");
|
||||
}
|
||||
Err(TrySendError::Full(_)) => {
|
||||
panic!("queue is full");
|
||||
}
|
||||
Ok(_) => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
ReadableStream::create_with_stream_bytes(env, ReceiverStream::new(rx))
|
||||
}
|
||||
@ -100,10 +100,10 @@ pub fn tsfn_call_with_callback(tsfn: ThreadsafeFunction<(), String>) -> napi::Re
|
||||
}
|
||||
|
||||
#[napi(ts_return_type = "Promise<void>")]
|
||||
pub fn tsfn_async_call(
|
||||
env: Env,
|
||||
pub fn tsfn_async_call<'env>(
|
||||
env: &'env Env,
|
||||
func: Function<FnArgs<(u32, u32, u32)>, String>,
|
||||
) -> napi::Result<PromiseRaw<()>> {
|
||||
) -> napi::Result<PromiseRaw<'env, ()>> {
|
||||
let tsfn = func.build_threadsafe_function().build()?;
|
||||
|
||||
env.spawn_future(async move {
|
||||
|
||||
@ -32,7 +32,7 @@ pub fn create_external_buffer_slice(env: &Env) -> Result<BufferSlice> {
|
||||
// Mock the ffi data that not managed by Rust
|
||||
std::mem::forget(data);
|
||||
unsafe {
|
||||
BufferSlice::from_external(env, data_ptr, len, data_ptr, move |ptr, _| {
|
||||
BufferSlice::from_external(env, data_ptr, len, data_ptr, move |_, ptr| {
|
||||
std::mem::drop(Vec::from_raw_parts(ptr, len, len));
|
||||
})
|
||||
}
|
||||
@ -76,11 +76,12 @@ async fn buffer_pass_through(buf: Buffer) -> Result<Buffer> {
|
||||
}
|
||||
|
||||
#[napi]
|
||||
fn buffer_with_async_block(env: Env, buf: Arc<Buffer>) -> Result<AsyncBlock<u32>> {
|
||||
fn buffer_with_async_block(env: &Env, buf: Arc<Buffer>) -> Result<AsyncBlock<u32>> {
|
||||
let buf_to_dispose = buf.clone();
|
||||
AsyncBlockBuilder::with(async move { Ok(buf.len() as u32) })
|
||||
.with_dispose(move |_| {
|
||||
drop(buf_to_dispose);
|
||||
Ok(())
|
||||
})
|
||||
.build(env)
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ pub struct Room {
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn test_async(env: Env) -> napi::Result<napi::bindgen_prelude::PromiseRaw<String>> {
|
||||
pub fn test_async(env: &Env) -> napi::Result<napi::bindgen_prelude::PromiseRaw<String>> {
|
||||
let data = serde_json::json!({
|
||||
"findFirstBooking": {
|
||||
"id": "ckovh15xa104945sj64rdk8oas",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user