feat(napi): experimental iterator support
This commit is contained in:
parent
b074608582
commit
a3356264f2
24 changed files with 947 additions and 102 deletions
|
@ -21,6 +21,7 @@ pub struct NapiFn {
|
||||||
pub ts_return_type: Option<String>,
|
pub ts_return_type: Option<String>,
|
||||||
pub skip_typescript: bool,
|
pub skip_typescript: bool,
|
||||||
pub comments: Vec<String>,
|
pub comments: Vec<String>,
|
||||||
|
pub parent_is_generator: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -62,6 +63,7 @@ pub struct NapiStruct {
|
||||||
pub kind: NapiStructKind,
|
pub kind: NapiStructKind,
|
||||||
pub js_mod: Option<String>,
|
pub js_mod: Option<String>,
|
||||||
pub comments: Vec<String>,
|
pub comments: Vec<String>,
|
||||||
|
pub implement_iterator: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -89,6 +91,9 @@ pub struct NapiImpl {
|
||||||
pub js_name: String,
|
pub js_name: String,
|
||||||
pub items: Vec<NapiFn>,
|
pub items: Vec<NapiFn>,
|
||||||
pub task_output_type: Option<Type>,
|
pub task_output_type: Option<Type>,
|
||||||
|
pub iterator_yield_type: Option<Type>,
|
||||||
|
pub iterator_next_type: Option<Type>,
|
||||||
|
pub iterator_return_type: Option<Type>,
|
||||||
pub js_mod: Option<String>,
|
pub js_mod: Option<String>,
|
||||||
pub comments: Vec<String>,
|
pub comments: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ impl NapiFn {
|
||||||
} else {
|
} else {
|
||||||
if self.parent.is_some() {
|
if self.parent.is_some() {
|
||||||
if let syn::Type::Path(path) = path.ty.as_ref() {
|
if let syn::Type::Path(path) = path.ty.as_ref() {
|
||||||
if let Some(p) = path.path.segments.first() {
|
if let Some(p) = path.path.segments.last() {
|
||||||
if p.ident == "Reference" {
|
if p.ident == "Reference" {
|
||||||
if let syn::PathArguments::AngleBracketed(
|
if let syn::PathArguments::AngleBracketed(
|
||||||
syn::AngleBracketedGenericArguments { args: angle_bracketed_args, .. },
|
syn::AngleBracketedGenericArguments { args: angle_bracketed_args, .. },
|
||||||
|
@ -281,7 +281,13 @@ impl NapiFn {
|
||||||
let is_return_self = ty_string == "& Self" || ty_string == "&mut Self";
|
let is_return_self = ty_string == "& Self" || ty_string == "&mut Self";
|
||||||
if self.kind == FnKind::Constructor {
|
if self.kind == FnKind::Constructor {
|
||||||
if self.is_ret_result {
|
if self.is_ret_result {
|
||||||
|
if self.parent_is_generator {
|
||||||
|
quote! { cb.construct_generator(#js_name, #ret?) }
|
||||||
|
} else {
|
||||||
quote! { cb.construct(#js_name, #ret?) }
|
quote! { cb.construct(#js_name, #ret?) }
|
||||||
|
}
|
||||||
|
} else if self.parent_is_generator {
|
||||||
|
quote! { cb.construct_generator(#js_name, #ret) }
|
||||||
} else {
|
} else {
|
||||||
quote! { cb.construct(#js_name, #ret) }
|
quote! { cb.construct(#js_name, #ret) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,13 +186,19 @@ impl NapiStruct {
|
||||||
quote! { #name {#(#fields),*} }
|
quote! { #name {#(#fields),*} }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let constructor = if self.implement_iterator {
|
||||||
|
quote! { unsafe { cb.construct_generator(#js_name_str, #construct) } }
|
||||||
|
} else {
|
||||||
|
quote! { unsafe { cb.construct(#js_name_str, #construct) } }
|
||||||
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
extern "C" fn constructor(
|
extern "C" fn constructor(
|
||||||
env: napi::bindgen_prelude::sys::napi_env,
|
env: napi::bindgen_prelude::sys::napi_env,
|
||||||
cb: napi::bindgen_prelude::sys::napi_callback_info
|
cb: napi::bindgen_prelude::sys::napi_callback_info
|
||||||
) -> napi::bindgen_prelude::sys::napi_value {
|
) -> napi::bindgen_prelude::sys::napi_value {
|
||||||
napi::bindgen_prelude::CallbackInfo::<#fields_len>::new(env, cb, None)
|
napi::bindgen_prelude::CallbackInfo::<#fields_len>::new(env, cb, None)
|
||||||
.and_then(|cb| unsafe { cb.construct(#js_name_str, #construct) })
|
.and_then(|cb| #constructor)
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
|
unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
|
||||||
std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
|
std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
|
||||||
|
@ -218,18 +224,21 @@ impl NapiStruct {
|
||||||
let name = &self.name;
|
let name = &self.name;
|
||||||
let js_name_raw = &self.js_name;
|
let js_name_raw = &self.js_name;
|
||||||
let js_name_str = format!("{}\0", js_name_raw);
|
let js_name_str = format!("{}\0", js_name_raw);
|
||||||
|
let iterator_implementation = self.gen_iterator_property(name);
|
||||||
quote! {
|
quote! {
|
||||||
impl napi::bindgen_prelude::ToNapiValue for #name {
|
impl napi::bindgen_prelude::ToNapiValue for #name {
|
||||||
unsafe fn to_napi_value(
|
unsafe fn to_napi_value(
|
||||||
env: napi::sys::napi_env,
|
env: napi::sys::napi_env,
|
||||||
val: #name
|
val: #name
|
||||||
) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
|
) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
|
||||||
if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
|
if let Some(ctor_ref) = napi::__private::get_class_constructor(#js_name_str) {
|
||||||
let wrapped_value = Box::into_raw(Box::new(val)) as *mut std::ffi::c_void;
|
let wrapped_value = Box::into_raw(Box::new(val));
|
||||||
#name::new_instance(env, wrapped_value, ctor_ref)
|
let instance_value = #name::new_instance(env, wrapped_value as *mut std::ffi::c_void, ctor_ref)?;
|
||||||
|
#iterator_implementation
|
||||||
|
Ok(instance_value)
|
||||||
} else {
|
} else {
|
||||||
Err(napi::bindgen_prelude::Error::new(
|
Err(napi::bindgen_prelude::Error::new(
|
||||||
napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
|
napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}` in `ToNapiValue`", #js_name_raw))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,9 +248,13 @@ impl NapiStruct {
|
||||||
pub fn into_reference(val: #name, env: napi::Env) -> napi::Result<napi::bindgen_prelude::Reference<#name>> {
|
pub fn into_reference(val: #name, env: napi::Env) -> napi::Result<napi::bindgen_prelude::Reference<#name>> {
|
||||||
if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
|
if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let wrapped_value = Box::into_raw(Box::new(val)) as *mut std::ffi::c_void;
|
let wrapped_value = Box::into_raw(Box::new(val));
|
||||||
#name::new_instance(env.raw(), wrapped_value, ctor_ref)?;
|
let instance_value = #name::new_instance(env.raw(), wrapped_value as *mut std::ffi::c_void, ctor_ref)?;
|
||||||
napi::bindgen_prelude::Reference::<#name>::from_value_ptr(wrapped_value, env.raw())
|
{
|
||||||
|
let env = env.raw();
|
||||||
|
#iterator_implementation
|
||||||
|
}
|
||||||
|
napi::bindgen_prelude::Reference::<#name>::from_value_ptr(wrapped_value as *mut std::ffi::c_void, env.raw())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(napi::bindgen_prelude::Error::new(
|
Err(napi::bindgen_prelude::Error::new(
|
||||||
|
@ -258,7 +271,7 @@ impl NapiStruct {
|
||||||
let mut ctor = std::ptr::null_mut();
|
let mut ctor = std::ptr::null_mut();
|
||||||
napi::check_status!(
|
napi::check_status!(
|
||||||
napi::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
|
napi::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
|
||||||
"Failed to get constructor of class `{}`",
|
"Failed to get constructor reference of class `{}`",
|
||||||
#js_name_raw
|
#js_name_raw
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -292,6 +305,15 @@ impl NapiStruct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gen_iterator_property(&self, name: &Ident) -> TokenStream {
|
||||||
|
if !self.implement_iterator {
|
||||||
|
return quote! {};
|
||||||
|
}
|
||||||
|
quote! {
|
||||||
|
napi::__private::create_iterator::<#name>(env, instance_value, wrapped_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn gen_to_napi_value_ctor_impl(&self) -> TokenStream {
|
fn gen_to_napi_value_ctor_impl(&self) -> TokenStream {
|
||||||
let name = &self.name;
|
let name = &self.name;
|
||||||
let js_name_str = format!("{}\0", &self.js_name);
|
let js_name_str = format!("{}\0", &self.js_name);
|
||||||
|
@ -331,28 +353,29 @@ impl NapiStruct {
|
||||||
quote! {
|
quote! {
|
||||||
impl napi::bindgen_prelude::ToNapiValue for #name {
|
impl napi::bindgen_prelude::ToNapiValue for #name {
|
||||||
unsafe fn to_napi_value(
|
unsafe fn to_napi_value(
|
||||||
env: napi::bindgen_prelude::sys::napi_env, val: #name
|
env: napi::bindgen_prelude::sys::napi_env,
|
||||||
|
val: #name,
|
||||||
) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
|
) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
|
||||||
if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
|
if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
|
||||||
let mut ctor = std::ptr::null_mut();
|
let mut ctor = std::ptr::null_mut();
|
||||||
|
|
||||||
napi::bindgen_prelude::check_status!(
|
napi::bindgen_prelude::check_status!(
|
||||||
napi::bindgen_prelude::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
|
napi::bindgen_prelude::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
|
||||||
"Failed to get constructor of class `{}`",
|
"Failed to get constructor reference of class `{}`",
|
||||||
#js_name_str
|
#js_name_str
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut result = std::ptr::null_mut();
|
let mut instance_value = std::ptr::null_mut();
|
||||||
let #destructed_fields = val;
|
let #destructed_fields = val;
|
||||||
let args = vec![#(#field_conversions),*];
|
let args = vec![#(#field_conversions),*];
|
||||||
|
|
||||||
napi::bindgen_prelude::check_status!(
|
napi::bindgen_prelude::check_status!(
|
||||||
napi::bindgen_prelude::sys::napi_new_instance(env, ctor, args.len(), args.as_ptr(), &mut result),
|
napi::bindgen_prelude::sys::napi_new_instance(env, ctor, args.len(), args.as_ptr(), &mut instance_value),
|
||||||
"Failed to construct class `{}`",
|
"Failed to construct class `{}`",
|
||||||
#js_name_str
|
#js_name_str
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(result)
|
Ok(instance_value)
|
||||||
} else {
|
} else {
|
||||||
Err(napi::bindgen_prelude::Error::new(
|
Err(napi::bindgen_prelude::Error::new(
|
||||||
napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_str))
|
napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_str))
|
||||||
|
@ -601,7 +624,7 @@ impl NapiStruct {
|
||||||
#[cfg(all(not(test), not(feature = "noop")))]
|
#[cfg(all(not(test), not(feature = "noop")))]
|
||||||
#[napi::bindgen_prelude::ctor]
|
#[napi::bindgen_prelude::ctor]
|
||||||
fn #struct_register_name() {
|
fn #struct_register_name() {
|
||||||
napi::bindgen_prelude::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -675,7 +698,7 @@ impl NapiImpl {
|
||||||
#[cfg(all(not(test), not(feature = "noop")))]
|
#[cfg(all(not(test), not(feature = "noop")))]
|
||||||
#[napi::bindgen_prelude::ctor]
|
#[napi::bindgen_prelude::ctor]
|
||||||
fn #register_name() {
|
fn #register_name() {
|
||||||
napi::bindgen_prelude::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub struct Napi {
|
||||||
macro_rules! napi_ast_impl {
|
macro_rules! napi_ast_impl {
|
||||||
( $( ($v:ident, $ast:ident), )* ) => {
|
( $( ($v:ident, $ast:ident), )* ) => {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum NapiItem {
|
pub enum NapiItem {
|
||||||
$($v($ast)),*
|
$($v($ast)),*
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,7 +179,6 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||||
("Either3", "{} | {} | {}"),
|
("Either3", "{} | {} | {}"),
|
||||||
("Either4", "{} | {} | {} | {}"),
|
("Either4", "{} | {} | {} | {}"),
|
||||||
("Either5", "{} | {} | {} | {} | {}"),
|
("Either5", "{} | {} | {} | {} | {}"),
|
||||||
("unknown", "unknown"),
|
|
||||||
("Null", "null"),
|
("Null", "null"),
|
||||||
("JsNull", "null"),
|
("JsNull", "null"),
|
||||||
("null", "null"),
|
("null", "null"),
|
||||||
|
@ -190,6 +189,8 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||||
("JsFunction", "(...args: any[]) => any"),
|
("JsFunction", "(...args: any[]) => any"),
|
||||||
("JsGlobal", "typeof global"),
|
("JsGlobal", "typeof global"),
|
||||||
("External", "ExternalObject<{}>"),
|
("External", "ExternalObject<{}>"),
|
||||||
|
("unknown", "unknown"),
|
||||||
|
("Unknown", "unknown"),
|
||||||
("JsUnknown", "unknown"),
|
("JsUnknown", "unknown"),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -281,12 +282,18 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S
|
||||||
});
|
});
|
||||||
} else if rust_ty == "Reference" {
|
} else if rust_ty == "Reference" {
|
||||||
ts_ty = r#struct::TASK_STRUCTS.with(|t| {
|
ts_ty = r#struct::TASK_STRUCTS.with(|t| {
|
||||||
let (output_type, _) = args.first().unwrap().to_owned();
|
// Reference<T> => T
|
||||||
|
if let Some(arg) = args.first() {
|
||||||
|
let (output_type, _) = arg.to_owned();
|
||||||
if let Some(o) = t.borrow().get(&output_type) {
|
if let Some(o) = t.borrow().get(&output_type) {
|
||||||
Some((o.to_owned(), false))
|
Some((o.to_owned(), false))
|
||||||
} else {
|
} else {
|
||||||
Some((output_type, false))
|
Some((output_type, false))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Not NAPI-RS `Reference`
|
||||||
|
Some((rust_ty, false))
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else if let Some(&known_ty) = KNOWN_TYPES.get(rust_ty.as_str()) {
|
} else if let Some(&known_ty) = KNOWN_TYPES.get(rust_ty.as_str()) {
|
||||||
if known_ty.contains("{}") {
|
if known_ty.contains("{}") {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use convert_case::{Case, Casing};
|
use convert_case::{Case, Casing};
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use syn::Pat;
|
use syn::{Pat, PathArguments, PathSegment};
|
||||||
|
|
||||||
use super::{ty_to_ts_type, ToTypeDef, TypeDef};
|
use super::{ty_to_ts_type, ToTypeDef, TypeDef};
|
||||||
use crate::{js_doc_from_comments, CallbackArg, FnKind, NapiFn};
|
use crate::{js_doc_from_comments, CallbackArg, FnKind, NapiFn};
|
||||||
|
@ -122,9 +122,21 @@ impl NapiFn {
|
||||||
.filter_map(|arg| match arg {
|
.filter_map(|arg| match arg {
|
||||||
crate::NapiFnArgKind::PatType(path) => {
|
crate::NapiFnArgKind::PatType(path) => {
|
||||||
let ty_string = path.ty.to_token_stream().to_string();
|
let ty_string = path.ty.to_token_stream().to_string();
|
||||||
if ty_string == "Env" || ty_string.replace(' ', "").starts_with("Reference<") {
|
if ty_string == "Env" {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
if let syn::Type::Path(path) = path.ty.as_ref() {
|
||||||
|
if let Some(PathSegment {
|
||||||
|
ident,
|
||||||
|
arguments: PathArguments::AngleBracketed(_),
|
||||||
|
}) = path.path.segments.last()
|
||||||
|
{
|
||||||
|
if ident == "Reference" {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut path = path.clone();
|
let mut path = path.clone();
|
||||||
// remove mutability from PatIdent
|
// remove mutability from PatIdent
|
||||||
if let Pat::Ident(i) = path.pat.as_mut() {
|
if let Pat::Ident(i) = path.pat.as_mut() {
|
||||||
|
|
|
@ -43,6 +43,31 @@ impl ToTypeDef for NapiImpl {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(output_type) = &self.iterator_yield_type {
|
||||||
|
let next_type = if let Some(ref ty) = self.iterator_next_type {
|
||||||
|
ty_to_ts_type(ty, false, false).0
|
||||||
|
} else {
|
||||||
|
"void".to_owned()
|
||||||
|
};
|
||||||
|
let return_type = if let Some(ref ty) = self.iterator_return_type {
|
||||||
|
ty_to_ts_type(ty, false, false).0
|
||||||
|
} else {
|
||||||
|
"void".to_owned()
|
||||||
|
};
|
||||||
|
Some(TypeDef {
|
||||||
|
kind: "impl".to_owned(),
|
||||||
|
name: self.js_name.to_owned(),
|
||||||
|
original_name: None,
|
||||||
|
def: format!(
|
||||||
|
"[Symbol.iterator](): Iterator<{}, {}, {}>",
|
||||||
|
ty_to_ts_type(output_type, false, true).0,
|
||||||
|
return_type,
|
||||||
|
next_type,
|
||||||
|
),
|
||||||
|
js_mod: self.js_mod.to_owned(),
|
||||||
|
js_doc: "".to_string(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
Some(TypeDef {
|
Some(TypeDef {
|
||||||
kind: "impl".to_owned(),
|
kind: "impl".to_owned(),
|
||||||
name: self.js_name.to_owned(),
|
name: self.js_name.to_owned(),
|
||||||
|
@ -68,6 +93,7 @@ impl ToTypeDef for NapiImpl {
|
||||||
js_doc: "".to_string(),
|
js_doc: "".to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NapiStruct {
|
impl NapiStruct {
|
||||||
|
|
|
@ -52,6 +52,7 @@ macro_rules! attrgen {
|
||||||
(strict, Strict(Span)),
|
(strict, Strict(Span)),
|
||||||
(object, Object(Span)),
|
(object, Object(Span)),
|
||||||
(namespace, Namespace(Span, String, Span)),
|
(namespace, Namespace(Span, String, Span)),
|
||||||
|
(iterator, Iterator(Span)),
|
||||||
(ts_args_type, TsArgsType(Span, String, Span)),
|
(ts_args_type, TsArgsType(Span, String, Span)),
|
||||||
(ts_return_type, TsReturnType(Span, String, Span)),
|
(ts_return_type, TsReturnType(Span, String, Span)),
|
||||||
(ts_type, TsType(Span, String, Span)),
|
(ts_type, TsType(Span, String, Span)),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod attrs;
|
pub mod attrs;
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::str::Chars;
|
use std::str::Chars;
|
||||||
|
|
||||||
|
@ -17,10 +17,14 @@ use proc_macro2::{Ident, TokenStream, TokenTree};
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use syn::ext::IdentExt;
|
use syn::ext::IdentExt;
|
||||||
use syn::parse::{Parse, ParseStream, Result as SynResult};
|
use syn::parse::{Parse, ParseStream, Result as SynResult};
|
||||||
use syn::{Attribute, Signature, Type, Visibility};
|
use syn::{Attribute, PathSegment, Signature, Type, Visibility};
|
||||||
|
|
||||||
use crate::parser::attrs::{check_recorded_struct_for_impl, record_struct};
|
use crate::parser::attrs::{check_recorded_struct_for_impl, record_struct};
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static GENERATOR_STRUCT: RefCell<HashMap<String, bool>> = Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
struct AnyIdent(Ident);
|
struct AnyIdent(Ident);
|
||||||
|
|
||||||
impl Parse for AnyIdent {
|
impl Parse for AnyIdent {
|
||||||
|
@ -562,6 +566,20 @@ fn napi_fn_from_decl(
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let namespace = opts.namespace().map(|(m, _)| m.to_owned());
|
||||||
|
let parent_is_generator = if let Some(p) = parent {
|
||||||
|
GENERATOR_STRUCT.with(|inner| {
|
||||||
|
let inner = inner.borrow();
|
||||||
|
let key = namespace
|
||||||
|
.as_ref()
|
||||||
|
.map(|n| format!("{}::{}", n, p))
|
||||||
|
.unwrap_or_else(|| p.to_string());
|
||||||
|
*inner.get(&key).unwrap_or(&false)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
NapiFn {
|
NapiFn {
|
||||||
name: ident,
|
name: ident,
|
||||||
js_name,
|
js_name,
|
||||||
|
@ -581,6 +599,7 @@ fn napi_fn_from_decl(
|
||||||
ts_args_type: opts.ts_args_type().map(|(m, _)| m.to_owned()),
|
ts_args_type: opts.ts_args_type().map(|(m, _)| m.to_owned()),
|
||||||
ts_return_type: opts.ts_return_type().map(|(m, _)| m.to_owned()),
|
ts_return_type: opts.ts_return_type().map(|(m, _)| m.to_owned()),
|
||||||
skip_typescript: opts.skip_typescript().is_some(),
|
skip_typescript: opts.skip_typescript().is_some(),
|
||||||
|
parent_is_generator,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -786,6 +805,16 @@ impl ConvertToAST for syn::ItemStruct {
|
||||||
}
|
}
|
||||||
|
|
||||||
record_struct(&struct_name, js_name.clone(), &opts);
|
record_struct(&struct_name, js_name.clone(), &opts);
|
||||||
|
let namespace = opts.namespace().map(|(m, _)| m.to_owned());
|
||||||
|
let implement_iterator = opts.iterator().is_some();
|
||||||
|
GENERATOR_STRUCT.with(|inner| {
|
||||||
|
let mut inner = inner.borrow_mut();
|
||||||
|
let key = namespace
|
||||||
|
.as_ref()
|
||||||
|
.map(|n| format!("{}::{}", n, struct_name))
|
||||||
|
.unwrap_or_else(|| struct_name.to_string());
|
||||||
|
inner.insert(key, implement_iterator);
|
||||||
|
});
|
||||||
|
|
||||||
Diagnostic::from_vec(errors).map(|()| Napi {
|
Diagnostic::from_vec(errors).map(|()| Napi {
|
||||||
item: NapiItem::Struct(NapiStruct {
|
item: NapiItem::Struct(NapiStruct {
|
||||||
|
@ -795,8 +824,9 @@ impl ConvertToAST for syn::ItemStruct {
|
||||||
fields,
|
fields,
|
||||||
is_tuple,
|
is_tuple,
|
||||||
kind: struct_kind,
|
kind: struct_kind,
|
||||||
js_mod: opts.namespace().map(|(m, _)| m.to_owned()),
|
js_mod: namespace,
|
||||||
comments: extract_doc_comments(&self.attrs),
|
comments: extract_doc_comments(&self.attrs),
|
||||||
|
implement_iterator,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -819,14 +849,31 @@ impl ConvertToAST for syn::ItemImpl {
|
||||||
let mut struct_js_name = struct_name.to_string().to_case(Case::UpperCamel);
|
let mut struct_js_name = struct_name.to_string().to_case(Case::UpperCamel);
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
let mut task_output_type = None;
|
let mut task_output_type = None;
|
||||||
|
let mut iterator_yield_type = None;
|
||||||
|
let mut iterator_next_type = None;
|
||||||
|
let mut iterator_return_type = None;
|
||||||
for item in self.items.iter_mut() {
|
for item in self.items.iter_mut() {
|
||||||
if let Some(method) = match item {
|
if let Some(method) = match item {
|
||||||
syn::ImplItem::Method(m) => Some(m),
|
syn::ImplItem::Method(m) => Some(m),
|
||||||
syn::ImplItem::Type(m) => {
|
syn::ImplItem::Type(m) => {
|
||||||
if m.ident == *"JsValue" {
|
if let Some((_, t, _)) = &self.trait_ {
|
||||||
|
if let Some(PathSegment { ident, .. }) = t.segments.last() {
|
||||||
|
if ident == "Task" && m.ident == "JsValue" {
|
||||||
if let Type::Path(_) = &m.ty {
|
if let Type::Path(_) = &m.ty {
|
||||||
task_output_type = Some(m.ty.clone());
|
task_output_type = Some(m.ty.clone());
|
||||||
}
|
}
|
||||||
|
} else if ident == "Generator" {
|
||||||
|
if let Type::Path(_) = &m.ty {
|
||||||
|
if m.ident == "Yield" {
|
||||||
|
iterator_yield_type = Some(m.ty.clone());
|
||||||
|
} else if m.ident == "Next" {
|
||||||
|
iterator_next_type = Some(m.ty.clone());
|
||||||
|
} else if m.ident == "Return" {
|
||||||
|
iterator_return_type = Some(m.ty.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -866,13 +913,18 @@ impl ConvertToAST for syn::ItemImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let namespace = impl_opts.namespace().map(|(m, _)| m.to_owned());
|
||||||
|
|
||||||
Ok(Napi {
|
Ok(Napi {
|
||||||
item: NapiItem::Impl(NapiImpl {
|
item: NapiItem::Impl(NapiImpl {
|
||||||
name: struct_name,
|
name: struct_name,
|
||||||
js_name: struct_js_name,
|
js_name: struct_js_name,
|
||||||
items,
|
items,
|
||||||
task_output_type,
|
task_output_type,
|
||||||
js_mod: impl_opts.namespace().map(|(m, _)| m.to_owned()),
|
iterator_yield_type,
|
||||||
|
iterator_next_type,
|
||||||
|
iterator_return_type,
|
||||||
|
js_mod: namespace,
|
||||||
comments: extract_doc_comments(&self.attrs),
|
comments: extract_doc_comments(&self.attrs),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
|
@ -66,10 +66,10 @@ impl<const N: usize> CallbackInfo<N> {
|
||||||
self.this
|
self.this
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn construct<T: 'static>(&self, js_name: &str, obj: T) -> Result<sys::napi_value> {
|
fn _construct<T: 'static>(&self, js_name: &str, obj: T) -> Result<(sys::napi_value, *mut T)> {
|
||||||
let obj = Box::new(obj);
|
let obj = Box::new(obj);
|
||||||
let this = self.this();
|
let this = self.this();
|
||||||
let value_ref = Box::into_raw(obj) as *mut c_void;
|
let value_ref = Box::into_raw(obj);
|
||||||
let mut object_ref = ptr::null_mut();
|
let mut object_ref = ptr::null_mut();
|
||||||
let initial_finalize: Box<dyn FnOnce()> = Box::new(|| {});
|
let initial_finalize: Box<dyn FnOnce()> = Box::new(|| {});
|
||||||
let finalize_callbacks_ptr = Rc::into_raw(Rc::new(Cell::new(Box::into_raw(initial_finalize))));
|
let finalize_callbacks_ptr = Rc::into_raw(Rc::new(Cell::new(Box::into_raw(initial_finalize))));
|
||||||
|
@ -78,7 +78,7 @@ impl<const N: usize> CallbackInfo<N> {
|
||||||
sys::napi_wrap(
|
sys::napi_wrap(
|
||||||
self.env,
|
self.env,
|
||||||
this,
|
this,
|
||||||
value_ref,
|
value_ref as *mut c_void,
|
||||||
Some(raw_finalize_unchecked::<T>),
|
Some(raw_finalize_unchecked::<T>),
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
&mut object_ref
|
&mut object_ref
|
||||||
|
@ -88,8 +88,25 @@ impl<const N: usize> CallbackInfo<N> {
|
||||||
)?;
|
)?;
|
||||||
};
|
};
|
||||||
|
|
||||||
Reference::<T>::add_ref(value_ref, (value_ref, object_ref, finalize_callbacks_ptr));
|
Reference::<T>::add_ref(
|
||||||
Ok(this)
|
value_ref as *mut c_void,
|
||||||
|
(value_ref as *mut c_void, object_ref, finalize_callbacks_ptr),
|
||||||
|
);
|
||||||
|
Ok((this, value_ref))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn construct<T: 'static>(&self, js_name: &str, obj: T) -> Result<sys::napi_value> {
|
||||||
|
self._construct(js_name, obj).map(|(v, _)| v)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn construct_generator<T: Generator + 'static>(
|
||||||
|
&self,
|
||||||
|
js_name: &str,
|
||||||
|
obj: T,
|
||||||
|
) -> Result<sys::napi_value> {
|
||||||
|
let (instance, generator_ptr) = self._construct(js_name, obj)?;
|
||||||
|
crate::__private::create_iterator(self.env, instance, generator_ptr);
|
||||||
|
Ok(instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn factory<T: 'static>(&self, js_name: &str, obj: T) -> Result<sys::napi_value> {
|
pub fn factory<T: 'static>(&self, js_name: &str, obj: T) -> Result<sys::napi_value> {
|
||||||
|
|
|
@ -5,7 +5,6 @@ macro_rules! check_status_or_throw {
|
||||||
if let Err(e) = $crate::check_status!($code, $($msg)*) {
|
if let Err(e) = $crate::check_status!($code, $($msg)*) {
|
||||||
#[allow(unused_unsafe)]
|
#[allow(unused_unsafe)]
|
||||||
unsafe { $crate::JsError::from(e).throw_into($env) };
|
unsafe { $crate::JsError::from(e).throw_into($env) };
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
581
crates/napi/src/bindgen_runtime/iterator.rs
Normal file
581
crates/napi/src/bindgen_runtime/iterator.rs
Normal file
|
@ -0,0 +1,581 @@
|
||||||
|
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};
|
||||||
|
|
||||||
|
use super::{FromNapiValue, ToNapiValue};
|
||||||
|
|
||||||
|
const GENERATOR_STATE_KEY: &str = "[[GeneratorState]]\0";
|
||||||
|
|
||||||
|
/// Implement a Iterator for the JavaScript Class.
|
||||||
|
/// This feature is an experimental feature and is not yet stable.
|
||||||
|
pub trait Generator {
|
||||||
|
type Yield: ToNapiValue;
|
||||||
|
type Next: FromNapiValue;
|
||||||
|
type Return: FromNapiValue;
|
||||||
|
|
||||||
|
/// Handle the `Generator.next()`
|
||||||
|
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/next
|
||||||
|
fn next(&mut self, value: Option<Self::Next>) -> Option<Self::Yield>;
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
/// Implement complete to handle the `Generator.return()`
|
||||||
|
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/return
|
||||||
|
fn complete(&mut self, value: Option<Self::Return>) -> Option<Self::Yield> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
/// Implement catch to handle the `Generator.throw()`
|
||||||
|
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/throw
|
||||||
|
fn catch(&mut self, env: Env, value: Unknown) -> Result<Option<Self::Yield>, Unknown> {
|
||||||
|
Err(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||||
|
pub fn create_iterator<T: Generator>(
|
||||||
|
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,
|
||||||
|
"Symbol\0".as_ptr() as *const c_char,
|
||||||
|
&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,
|
||||||
|
"iterator\0".as_ptr() as *const c_char,
|
||||||
|
&mut iterator_symbol,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"Get Symbol.iterator failed",
|
||||||
|
);
|
||||||
|
let mut generator_function = ptr::null_mut();
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe {
|
||||||
|
sys::napi_create_function(
|
||||||
|
env,
|
||||||
|
"Iterator\0".as_ptr() as *const c_char,
|
||||||
|
8,
|
||||||
|
Some(symbol_generator::<T>),
|
||||||
|
generator_ptr as *mut c_void,
|
||||||
|
&mut generator_function,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"Create iterator function failed",
|
||||||
|
);
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe { sys::napi_set_property(env, instance, iterator_symbol, generator_function) },
|
||||||
|
"Failed to set Symbol.iterator on class instance",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub unsafe extern "C" fn symbol_generator<T: Generator>(
|
||||||
|
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,
|
||||||
|
"next\0".as_ptr() as *const c_char,
|
||||||
|
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,
|
||||||
|
"return\0".as_ptr() as *const c_char,
|
||||||
|
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,
|
||||||
|
"throw\0".as_ptr() as *const c_char,
|
||||||
|
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,
|
||||||
|
"next\0".as_ptr() as *const c_char,
|
||||||
|
next_function,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"Set next function on Generator object failed"
|
||||||
|
);
|
||||||
|
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe {
|
||||||
|
sys::napi_set_named_property(
|
||||||
|
env,
|
||||||
|
generator_object,
|
||||||
|
"return\0".as_ptr() as *const c_char,
|
||||||
|
return_function,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"Set return function on Generator object failed"
|
||||||
|
);
|
||||||
|
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe {
|
||||||
|
sys::napi_set_named_property(
|
||||||
|
env,
|
||||||
|
generator_object,
|
||||||
|
"throw\0".as_ptr() as *const c_char,
|
||||||
|
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"
|
||||||
|
);
|
||||||
|
|
||||||
|
let properties = vec![sys::napi_property_descriptor {
|
||||||
|
utf8name: GENERATOR_STATE_KEY.as_ptr() as *const c_char,
|
||||||
|
name: ptr::null_mut(),
|
||||||
|
method: None,
|
||||||
|
getter: None,
|
||||||
|
setter: None,
|
||||||
|
value: generator_state,
|
||||||
|
attributes: sys::PropertyAttributes::writable,
|
||||||
|
data: ptr::null_mut(),
|
||||||
|
}];
|
||||||
|
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe { sys::napi_define_properties(env, generator_object, 1, properties.as_ptr()) },
|
||||||
|
"Define properties on Generator object failed"
|
||||||
|
);
|
||||||
|
|
||||||
|
generator_object
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn generator_next<T: Generator>(
|
||||||
|
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 mut generator_state = ptr::null_mut();
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe {
|
||||||
|
sys::napi_get_named_property(
|
||||||
|
env,
|
||||||
|
this,
|
||||||
|
GENERATOR_STATE_KEY.as_ptr() as *const c_char,
|
||||||
|
&mut generator_state,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"Get generator state failed"
|
||||||
|
);
|
||||||
|
let mut completed = false;
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe { sys::napi_get_value_bool(env, generator_state, &mut completed) },
|
||||||
|
"Get generator state failed"
|
||||||
|
);
|
||||||
|
let mut result = std::ptr::null_mut();
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe { sys::napi_create_object(env, &mut result) },
|
||||||
|
"Failed to create iterator result object",
|
||||||
|
);
|
||||||
|
if !completed {
|
||||||
|
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() as *mut c_char,
|
||||||
|
e.reason.as_ptr() as *mut c_char,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(value) = item {
|
||||||
|
set_generator_value(env, result, value);
|
||||||
|
} else {
|
||||||
|
completed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut completed_value = ptr::null_mut();
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe { sys::napi_get_boolean(env, completed, &mut completed_value) },
|
||||||
|
"Failed to create completed value"
|
||||||
|
);
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe {
|
||||||
|
sys::napi_set_named_property(
|
||||||
|
env,
|
||||||
|
result,
|
||||||
|
"done\0".as_ptr() as *const std::os::raw::c_char,
|
||||||
|
completed_value,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"Failed to set iterator result done",
|
||||||
|
);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn generator_return<T: Generator>(
|
||||||
|
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)) };
|
||||||
|
if argc == 0 {
|
||||||
|
g.complete(None);
|
||||||
|
} else {
|
||||||
|
g.complete(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() as *mut c_char,
|
||||||
|
e.reason.as_ptr() as *mut c_char,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
return ptr::null_mut();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut generator_state = ptr::null_mut();
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe { sys::napi_get_boolean(env, true, &mut generator_state) },
|
||||||
|
"Create generator state failed"
|
||||||
|
);
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe {
|
||||||
|
sys::napi_set_named_property(
|
||||||
|
env,
|
||||||
|
this,
|
||||||
|
GENERATOR_STATE_KEY.as_ptr() as *const c_char,
|
||||||
|
generator_state,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"Get generator state failed"
|
||||||
|
);
|
||||||
|
let mut result = std::ptr::null_mut();
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe { sys::napi_create_object(env, &mut result) },
|
||||||
|
"Failed to create iterator result object",
|
||||||
|
);
|
||||||
|
if argc > 0 {
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe {
|
||||||
|
sys::napi_set_named_property(
|
||||||
|
env,
|
||||||
|
result,
|
||||||
|
"value\0".as_ptr() as *const std::os::raw::c_char,
|
||||||
|
argv[0],
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"Failed to set iterator result value",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe {
|
||||||
|
sys::napi_set_named_property(
|
||||||
|
env,
|
||||||
|
result,
|
||||||
|
"done\0".as_ptr() as *const std::os::raw::c_char,
|
||||||
|
generator_state,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"Failed to set iterator result done",
|
||||||
|
);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn generator_throw<T: Generator>(
|
||||||
|
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 catch_result = 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,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let mut result = ptr::null_mut();
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe { sys::napi_create_object(env, &mut result) },
|
||||||
|
"Failed to create iterator result object",
|
||||||
|
);
|
||||||
|
let mut generator_state = ptr::null_mut();
|
||||||
|
let mut generator_state_value = false;
|
||||||
|
match catch_result {
|
||||||
|
Err(e) => {
|
||||||
|
generator_state_value = true;
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe { sys::napi_get_boolean(env, generator_state_value, &mut generator_state) },
|
||||||
|
"Create generator state failed"
|
||||||
|
);
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe {
|
||||||
|
sys::napi_set_named_property(
|
||||||
|
env,
|
||||||
|
this,
|
||||||
|
GENERATOR_STATE_KEY.as_ptr() as *const c_char,
|
||||||
|
generator_state,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"Get generator state failed"
|
||||||
|
);
|
||||||
|
let throw_status = unsafe { sys::napi_throw(env, e.0.value) };
|
||||||
|
debug_assert!(
|
||||||
|
throw_status == sys::Status::napi_ok,
|
||||||
|
"Failed to throw error {}",
|
||||||
|
crate::Status::from(throw_status)
|
||||||
|
);
|
||||||
|
return ptr::null_mut();
|
||||||
|
}
|
||||||
|
Ok(Some(v)) => {
|
||||||
|
set_generator_value(env, result, v);
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
generator_state_value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe { sys::napi_get_boolean(env, generator_state_value, &mut generator_state) },
|
||||||
|
"Create generator state failed"
|
||||||
|
);
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe {
|
||||||
|
sys::napi_set_named_property(
|
||||||
|
env,
|
||||||
|
this,
|
||||||
|
GENERATOR_STATE_KEY.as_ptr() as *const c_char,
|
||||||
|
generator_state,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"Get generator state failed"
|
||||||
|
);
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe {
|
||||||
|
sys::napi_set_named_property(
|
||||||
|
env,
|
||||||
|
result,
|
||||||
|
"done\0".as_ptr() as *const c_char,
|
||||||
|
generator_state,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"Get generator state failed"
|
||||||
|
);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_generator_value<V: ToNapiValue>(env: sys::napi_env, result: sys::napi_value, value: V) {
|
||||||
|
match unsafe { ToNapiValue::to_napi_value(env, value) } {
|
||||||
|
Ok(val) => {
|
||||||
|
check_status_or_throw!(
|
||||||
|
env,
|
||||||
|
unsafe {
|
||||||
|
sys::napi_set_named_property(
|
||||||
|
env,
|
||||||
|
result,
|
||||||
|
"value\0".as_ptr() as *const std::os::raw::c_char,
|
||||||
|
val,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"Failed to set iterator result value",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
unsafe {
|
||||||
|
sys::napi_throw_error(
|
||||||
|
env,
|
||||||
|
format!("{}", e.status).as_ptr() as *mut c_char,
|
||||||
|
e.reason.as_ptr() as *mut c_char,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ mod symbol;
|
||||||
mod task;
|
mod task;
|
||||||
mod value_ref;
|
mod value_ref;
|
||||||
|
|
||||||
|
pub use crate::js_values::JsUnknown as Unknown;
|
||||||
#[cfg(feature = "napi5")]
|
#[cfg(feature = "napi5")]
|
||||||
pub use crate::JsDate as Date;
|
pub use crate::JsDate as Date;
|
||||||
pub use array::*;
|
pub use array::*;
|
||||||
|
@ -159,7 +160,7 @@ pub trait ValidateNapiValue: FromNapiValue + TypeName {
|
||||||
|
|
||||||
impl<T: TypeName> TypeName for Option<T> {
|
impl<T: TypeName> TypeName for Option<T> {
|
||||||
fn type_name() -> &'static str {
|
fn type_name() -> &'static str {
|
||||||
"Option"
|
T::type_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_type() -> ValueType {
|
fn value_type() -> ValueType {
|
||||||
|
|
|
@ -170,6 +170,10 @@ impl<T: 'static, S: 'static> SharedReference<T, S> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clone_owner(&self, env: Env) -> Result<Reference<T>> {
|
||||||
|
self.owner.clone(env)
|
||||||
|
}
|
||||||
|
|
||||||
/// Safety to share because caller can provide `Env`
|
/// Safety to share because caller can provide `Env`
|
||||||
pub fn share_with<U: 'static, F: FnOnce(&'static mut S) -> Result<U>>(
|
pub fn share_with<U: 'static, F: FnOnce(&'static mut S) -> Result<U>>(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -5,6 +5,7 @@ use std::rc::Rc;
|
||||||
pub use callback_info::*;
|
pub use callback_info::*;
|
||||||
pub use ctor::ctor;
|
pub use ctor::ctor;
|
||||||
pub use env::*;
|
pub use env::*;
|
||||||
|
pub use iterator::Generator;
|
||||||
pub use js_values::*;
|
pub use js_values::*;
|
||||||
pub use module_register::*;
|
pub use module_register::*;
|
||||||
|
|
||||||
|
@ -14,12 +15,14 @@ use crate::Status;
|
||||||
mod callback_info;
|
mod callback_info;
|
||||||
mod env;
|
mod env;
|
||||||
mod error;
|
mod error;
|
||||||
|
pub mod iterator;
|
||||||
mod js_values;
|
mod js_values;
|
||||||
mod module_register;
|
mod module_register;
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// called when node wrapper objects destroyed
|
/// called when node wrapper objects destroyed
|
||||||
|
#[doc(hidden)]
|
||||||
pub unsafe extern "C" fn raw_finalize_unchecked<T>(
|
pub unsafe extern "C" fn raw_finalize_unchecked<T>(
|
||||||
env: sys::napi_env,
|
env: sys::napi_env,
|
||||||
finalize_data: *mut c_void,
|
finalize_data: *mut c_void,
|
||||||
|
@ -63,6 +66,7 @@ pub unsafe extern "C" fn raw_finalize_unchecked<T>(
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// called when node buffer is ready for gc
|
/// called when node buffer is ready for gc
|
||||||
|
#[doc(hidden)]
|
||||||
pub unsafe extern "C" fn drop_buffer(
|
pub unsafe extern "C" fn drop_buffer(
|
||||||
_env: sys::napi_env,
|
_env: sys::napi_env,
|
||||||
finalize_data: *mut c_void,
|
finalize_data: *mut c_void,
|
||||||
|
|
|
@ -376,11 +376,7 @@ unsafe extern "C" fn napi_register_module_v1(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (ctor, props): (Vec<_>, Vec<_>) = props.iter().partition(|prop| prop.is_ctor);
|
let (ctor, props): (Vec<_>, Vec<_>) = props.iter().partition(|prop| prop.is_ctor);
|
||||||
// one or more or zero?
|
|
||||||
// zero is for `#[napi(task)]`
|
|
||||||
if ctor.is_empty() && props.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let ctor = ctor.get(0).map(|c| c.raw().method.unwrap()).unwrap_or(noop);
|
let ctor = ctor.get(0).map(|c| c.raw().method.unwrap()).unwrap_or(noop);
|
||||||
let raw_props: Vec<_> = props.iter().map(|prop| prop.raw()).collect();
|
let raw_props: Vec<_> = props.iter().map(|prop| prop.raw()).collect();
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,7 @@ pub use napi_sys as sys;
|
||||||
pub use async_work::AsyncWorkPromise;
|
pub use async_work::AsyncWorkPromise;
|
||||||
pub use call_context::CallContext;
|
pub use call_context::CallContext;
|
||||||
|
|
||||||
|
pub use bindgen_runtime::iterator;
|
||||||
pub use env::*;
|
pub use env::*;
|
||||||
pub use error::*;
|
pub use error::*;
|
||||||
pub use js_values::*;
|
pub use js_values::*;
|
||||||
|
@ -154,13 +155,33 @@ macro_rules! assert_type_of {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub use crate::bindgen_runtime::ctor as module_init;
|
||||||
pub(crate) unsafe fn log_js_value<V: AsRef<[sys::napi_value]>>(
|
|
||||||
|
pub mod bindgen_prelude {
|
||||||
|
#[cfg(feature = "compat-mode")]
|
||||||
|
pub use crate::bindgen_runtime::register_module_exports;
|
||||||
|
#[cfg(feature = "tokio_rt")]
|
||||||
|
pub use crate::tokio_runtime::*;
|
||||||
|
pub use crate::{
|
||||||
|
assert_type_of, bindgen_runtime::*, check_status, check_status_or_throw, error, error::*, sys,
|
||||||
|
type_of, JsError, Property, PropertyAttributes, Result, Status, Task, ValueType,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod __private {
|
||||||
|
pub use crate::bindgen_runtime::{
|
||||||
|
get_class_constructor, iterator::create_iterator, register_class,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::sys;
|
||||||
|
|
||||||
|
pub unsafe fn log_js_value<V: AsRef<[sys::napi_value]>>(
|
||||||
// `info`, `log`, `warning` or `error`
|
// `info`, `log`, `warning` or `error`
|
||||||
method: &str,
|
method: &str,
|
||||||
env: sys::napi_env,
|
env: sys::napi_env,
|
||||||
values: V,
|
values: V,
|
||||||
) {
|
) {
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
|
@ -184,19 +205,7 @@ pub(crate) unsafe fn log_js_value<V: AsRef<[sys::napi_value]>>(
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use crate::bindgen_runtime::ctor as module_init;
|
|
||||||
|
|
||||||
pub mod bindgen_prelude {
|
|
||||||
#[cfg(feature = "compat-mode")]
|
|
||||||
pub use crate::bindgen_runtime::register_module_exports;
|
|
||||||
#[cfg(feature = "tokio_rt")]
|
|
||||||
pub use crate::tokio_runtime::*;
|
|
||||||
pub use crate::{
|
|
||||||
assert_type_of, bindgen_runtime::*, check_status, check_status_or_throw, error, error::*, sys,
|
|
||||||
type_of, JsError, Property, PropertyAttributes, Result, Status, Task, ValueType,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "tokio_rt")]
|
#[cfg(feature = "tokio_rt")]
|
||||||
|
|
51
examples/napi/__test__/generator.spec.ts
Normal file
51
examples/napi/__test__/generator.spec.ts
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import test from 'ava'
|
||||||
|
|
||||||
|
import { Fib } from '../index'
|
||||||
|
|
||||||
|
test('should be able to stop a generator', (t) => {
|
||||||
|
const fib = new Fib()
|
||||||
|
const gen = fib[Symbol.iterator]
|
||||||
|
t.is(typeof gen, 'function')
|
||||||
|
const iterator = gen()
|
||||||
|
t.deepEqual(iterator.next(), {
|
||||||
|
done: false,
|
||||||
|
value: 1,
|
||||||
|
})
|
||||||
|
iterator.next()
|
||||||
|
iterator.next()
|
||||||
|
iterator.next()
|
||||||
|
iterator.next()
|
||||||
|
t.deepEqual(iterator.next(), {
|
||||||
|
done: false,
|
||||||
|
value: 8,
|
||||||
|
})
|
||||||
|
t.deepEqual(iterator.return?.(), {
|
||||||
|
done: true,
|
||||||
|
})
|
||||||
|
t.deepEqual(iterator.next(), {
|
||||||
|
done: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should be able to throw to generator', (t) => {
|
||||||
|
const fib = new Fib()
|
||||||
|
const gen = fib[Symbol.iterator]
|
||||||
|
t.is(typeof gen, 'function')
|
||||||
|
const iterator = gen()
|
||||||
|
t.deepEqual(iterator.next(), {
|
||||||
|
done: false,
|
||||||
|
value: 1,
|
||||||
|
})
|
||||||
|
iterator.next()
|
||||||
|
iterator.next()
|
||||||
|
iterator.next()
|
||||||
|
iterator.next()
|
||||||
|
t.deepEqual(iterator.next(), {
|
||||||
|
done: false,
|
||||||
|
value: 8,
|
||||||
|
})
|
||||||
|
t.throws(() => iterator.throw!(new Error()))
|
||||||
|
t.deepEqual(iterator.next(), {
|
||||||
|
done: true,
|
||||||
|
})
|
||||||
|
})
|
|
@ -264,6 +264,10 @@ Generated by [AVA](https://avajs.dev).
|
||||||
export class JsClassForEither {␊
|
export class JsClassForEither {␊
|
||||||
constructor()␊
|
constructor()␊
|
||||||
}␊
|
}␊
|
||||||
|
export class Fib {␊
|
||||||
|
[Symbol.iterator](): Iterator<number, void, number>␊
|
||||||
|
constructor()␊
|
||||||
|
}␊
|
||||||
export class JsRepo {␊
|
export class JsRepo {␊
|
||||||
constructor(dir: string)␊
|
constructor(dir: string)␊
|
||||||
remote(): JsRemote␊
|
remote(): JsRemote␊
|
||||||
|
|
Binary file not shown.
4
examples/napi/index.d.ts
vendored
4
examples/napi/index.d.ts
vendored
|
@ -254,6 +254,10 @@ export class ClassWithFactory {
|
||||||
export class JsClassForEither {
|
export class JsClassForEither {
|
||||||
constructor()
|
constructor()
|
||||||
}
|
}
|
||||||
|
export class Fib {
|
||||||
|
[Symbol.iterator](): Iterator<number, void, number>
|
||||||
|
constructor()
|
||||||
|
}
|
||||||
export class JsRepo {
|
export class JsRepo {
|
||||||
constructor(dir: string)
|
constructor(dir: string)
|
||||||
remote(): JsRemote
|
remote(): JsRemote
|
||||||
|
|
42
examples/napi/src/generator.rs
Normal file
42
examples/napi/src/generator.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
use napi::bindgen_prelude::*;
|
||||||
|
|
||||||
|
#[napi(iterator)]
|
||||||
|
pub struct Fib {
|
||||||
|
current: u32,
|
||||||
|
next: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
impl Generator for Fib {
|
||||||
|
type Yield = u32;
|
||||||
|
type Next = i32;
|
||||||
|
type Return = ();
|
||||||
|
|
||||||
|
fn next(&mut self, value: Option<Self::Next>) -> Option<Self::Yield> {
|
||||||
|
match value {
|
||||||
|
Some(n) => {
|
||||||
|
self.current = n as u32;
|
||||||
|
self.next = n as u32 + 1;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let next = self.next;
|
||||||
|
let current = self.current;
|
||||||
|
self.current = next;
|
||||||
|
self.next = current + next;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Some(self.current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
|
impl Fib {
|
||||||
|
#[napi(constructor)]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Fib {
|
||||||
|
current: 0,
|
||||||
|
next: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ mod error;
|
||||||
mod external;
|
mod external;
|
||||||
mod fn_strict;
|
mod fn_strict;
|
||||||
mod fn_ts_override;
|
mod fn_ts_override;
|
||||||
|
mod generator;
|
||||||
mod js_mod;
|
mod js_mod;
|
||||||
mod map;
|
mod map;
|
||||||
mod nullable;
|
mod nullable;
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
|
|
||||||
use napi::bindgen_prelude::*;
|
use napi::bindgen_prelude::*;
|
||||||
use napi::Task;
|
|
||||||
|
|
||||||
struct DelaySum(u32, u32);
|
struct DelaySum(u32, u32);
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
impl Task for DelaySum {
|
impl napi::Task for DelaySum {
|
||||||
type Output = u32;
|
type Output = u32;
|
||||||
type JsValue = u32;
|
type JsValue = u32;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue