use crate::{api, prelude::*};
use std::mem::MaybeUninit;
#[derive(Clone, Debug)]
#[repr(C)]
pub struct NapiPropertyDescriptor(napi_property_descriptor);
impl AsRef<napi_property_descriptor> for NapiPropertyDescriptor {
fn as_ref(&self) -> &napi_property_descriptor {
&self.0
}
}
impl std::ops::Deref for NapiPropertyDescriptor {
type Target = napi_property_descriptor;
fn deref(&self) -> &napi_property_descriptor {
&self.0
}
}
impl NapiPropertyDescriptor {
pub fn raw(&self) -> &napi_property_descriptor {
&self.0
}
}
pub struct DescriptorValueBuilder {
pub utf8name: Option<String>,
pub name: napi_value,
pub value: napi_value,
pub attributes: NapiPropertyAttributes,
}
#[allow(clippy::type_complexity)]
pub struct DescriptorMethodBuilder<T: FromJsArgs, R: NapiValueT> {
pub utf8name: Option<String>,
pub name: napi_value,
pub method: Option<Box<dyn FnMut(JsObject, T) -> NapiResult<R> + 'static>>,
pub attributes: NapiPropertyAttributes,
}
#[allow(clippy::type_complexity)]
pub struct DescriptorAccessorBuilder<T: NapiValueT, R: NapiValueT> {
pub utf8name: Option<String>,
pub name: napi_value,
pub getter: Option<Box<dyn FnMut(JsObject) -> NapiResult<R> + 'static>>,
pub setter: Option<Box<dyn FnMut(JsObject, T) -> NapiResult<()> + 'static>>,
pub attributes: NapiPropertyAttributes,
}
impl DescriptorValueBuilder {
pub fn new() -> DescriptorValueBuilder {
DescriptorValueBuilder {
utf8name: None,
name: std::ptr::null_mut(),
value: std::ptr::null_mut(),
attributes: NapiPropertyAttributes::Default,
}
}
pub fn with_utf8name(mut self, name: impl Into<String>) -> Self {
self.utf8name.replace(name.into());
self
}
pub fn with_name(mut self, name: impl NapiValueT) -> Self {
let name = name.value();
if let (Ok(name_string), Ok(name_symbol)) = (
unsafe { name.cast::<JsString>() }.check(),
unsafe { name.cast::<JsSymbol>() }.check(),
) {
if name_string || name_symbol {
self.name = name.raw();
}
}
self
}
pub fn with_value(mut self, value: impl NapiValueT) -> Self {
self.value = value.raw();
self
}
pub fn with_attribute(mut self, attribute: NapiPropertyAttributes) -> Self {
self.attributes |= attribute;
self
}
pub fn build(mut self) -> NapiResult<NapiPropertyDescriptor> {
let utf8name = if let Some(name) = self.utf8name {
std::ffi::CString::new(name)
.map_err(|_| NapiStatus::StringExpected)?
.into_raw()
} else {
std::ptr::null()
};
let name = self.name;
if (utf8name.is_null() && name.is_null()) {
return Err(NapiStatus::InvalidArg);
}
let method = None;
let getter = None;
let setter = None;
let value = self.value;
let attributes = self.attributes.bits();
Ok(NapiPropertyDescriptor(napi_property_descriptor {
utf8name,
name,
method,
getter,
setter,
value,
attributes,
data: std::ptr::null_mut(),
}))
}
}
impl<T: FromJsArgs, R: NapiValueT> DescriptorMethodBuilder<T, R> {
pub fn new() -> Self {
Self {
utf8name: None,
name: std::ptr::null_mut(),
method: None,
attributes: NapiPropertyAttributes::Default,
}
}
pub fn with_utf8name(mut self, name: impl Into<String>) -> Self {
self.utf8name.replace(name.into());
self
}
pub fn with_name(mut self, name: impl NapiValueT) -> Self {
let name = name.value();
if let (Ok(name_string), Ok(name_symbol)) = (
unsafe { name.cast::<JsString>() }.check(),
unsafe { name.cast::<JsSymbol>() }.check(),
) {
if name_string || name_symbol {
self.name = name.raw();
}
}
self
}
pub fn with_method(
mut self,
method: impl FnMut(JsObject, T) -> NapiResult<R> + 'static,
) -> Self {
self.method = Some(Box::new(method));
self
}
pub fn with_attribute(mut self, attribute: NapiPropertyAttributes) -> Self {
self.attributes |= attribute;
self
}
#[allow(clippy::type_complexity)]
pub fn build(mut self) -> NapiResult<NapiPropertyDescriptor> {
let utf8name = if let Some(name) = self.utf8name {
std::ffi::CString::new(name)
.map_err(|_| NapiStatus::StringExpected)?
.into_raw()
} else {
std::ptr::null()
};
let name = self.name;
if (utf8name.is_null() && name.is_null()) {
return Err(NapiStatus::InvalidArg);
}
extern "C" fn method_trampoline<T: FromJsArgs, R: NapiValueT>(
env: NapiEnv,
info: napi_callback_info,
) -> napi_value {
let mut data = MaybeUninit::uninit();
let mut this = MaybeUninit::uninit();
let (argc, argv, this, mut func) = unsafe {
let mut argc = T::len();
let mut argv = vec![std::ptr::null_mut(); T::len()];
let status = api::napi_get_cb_info(
env,
info,
&mut argc,
argv.as_mut_ptr(),
this.as_mut_ptr(),
data.as_mut_ptr(),
);
let func: &mut Box<dyn FnMut(JsObject, T) -> NapiResult<R>> =
std::mem::transmute(data);
(argc, argv, this.assume_init(), func)
};
let args = argv
.into_iter()
.map(|arg| JsValue::from_raw(env, arg))
.collect();
let this = JsObject::from_raw(env, this);
if let Ok(args) = T::from_js_args(JsArgs(args)) {
napi_r!(env, =func(this, args))
} else {
env.throw_error("wrong argument type!").unwrap();
env.undefined().unwrap().raw()
}
}
let method = Some(method_trampoline::<T, R> as _);
let data = if let Some(method) = self.method.take() {
Box::into_raw(Box::new(method)) as _
} else {
return Err(NapiStatus::InvalidArg);
};
let getter = None;
let setter = None;
let value = std::ptr::null_mut();
let attributes = self.attributes.bits();
Ok(NapiPropertyDescriptor(napi_property_descriptor {
utf8name,
name,
method,
getter,
setter,
value,
attributes,
data,
}))
}
}
impl<T: NapiValueT, R: NapiValueT> DescriptorAccessorBuilder<T, R> {
pub fn new() -> Self {
Self {
utf8name: None,
name: std::ptr::null_mut(),
getter: None,
setter: None,
attributes: NapiPropertyAttributes::Default,
}
}
pub fn with_utf8name(mut self, name: impl Into<String>) -> Self {
self.utf8name.replace(name.into());
self
}
pub fn with_name(mut self, name: impl NapiValueT) -> Self {
let name = name.value();
if let (Ok(name_string), Ok(name_symbol)) = (
unsafe { name.cast::<JsString>() }.check(),
unsafe { name.cast::<JsSymbol>() }.check(),
) {
if name_string || name_symbol {
self.name = name.raw();
}
}
self
}
pub fn with_getter(mut self, getter: impl FnMut(JsObject) -> NapiResult<R> + 'static) -> Self {
self.getter = Some(Box::new(getter));
self
}
pub fn with_setter(
mut self,
setter: impl FnMut(JsObject, T) -> NapiResult<()> + 'static,
) -> Self {
self.setter = Some(Box::new(setter));
self
}
pub fn with_attribute(mut self, attribute: NapiPropertyAttributes) -> Self {
self.attributes |= attribute;
self
}
#[allow(clippy::type_complexity)]
pub fn build(mut self) -> NapiResult<NapiPropertyDescriptor> {
let utf8name = if let Some(name) = self.utf8name {
std::ffi::CString::new(name)
.map_err(|_| NapiStatus::StringExpected)?
.into_raw()
} else {
std::ptr::null()
};
let name = self.name;
if (utf8name.is_null() && name.is_null()) {
return Err(NapiStatus::InvalidArg);
}
extern "C" fn getter_trampoline<T: NapiValueT, R: NapiValueT>(
env: NapiEnv,
info: napi_callback_info,
) -> napi_value {
let mut argc = 0;
let mut argv = [std::ptr::null_mut(); 0];
let mut data = MaybeUninit::uninit();
let mut this = MaybeUninit::uninit();
let (argc, argv, this, mut func) = unsafe {
let status = api::napi_get_cb_info(
env,
info,
&mut argc,
argv.as_mut_ptr(),
this.as_mut_ptr(),
data.as_mut_ptr(),
);
let func: &mut (
Option<Box<dyn FnMut(JsObject) -> NapiResult<R>>>,
Option<Box<dyn FnMut(JsObject, T) -> NapiResult<()>>>,
) = std::mem::transmute(data);
(argc, argv, this.assume_init(), func)
};
let this = JsObject::from_raw(env, this);
napi_r!(env, =func.0.as_mut().unwrap()(this))
}
let mut data = (None, None);
let getter = if let Some(getter) = self.getter {
data.0 = Some(getter);
Some(getter_trampoline::<T, R> as _)
} else {
None
};
extern "C" fn setter_trampoline<T: NapiValueT, R: NapiValueT>(
env: NapiEnv,
info: napi_callback_info,
) -> napi_value {
let mut argc = 1;
let mut argv = [std::ptr::null_mut(); 1];
let mut data = MaybeUninit::uninit();
let mut this = MaybeUninit::uninit();
let (argc, argv, this, mut func) = unsafe {
let status = api::napi_get_cb_info(
env,
info,
&mut argc,
argv.as_mut_ptr(),
this.as_mut_ptr(),
data.as_mut_ptr(),
);
let func: &mut (
Option<Box<dyn FnMut(JsObject) -> NapiResult<R>>>,
Option<Box<dyn FnMut(JsObject, T) -> NapiResult<()>>>,
) = std::mem::transmute(data);
(argc, argv, this.assume_init(), func)
};
let value = T::from_raw(env, argv[0]);
let this = JsObject::from_raw(env, this);
napi_r!(env, func.1.as_mut().unwrap()(this, value))
}
let setter = if let Some(setter) = self.setter {
data.1 = Some(setter);
Some(setter_trampoline::<T, R> as _)
} else {
None
};
let attributes = self.attributes.bits();
let method = None;
let value = std::ptr::null_mut();
Ok(NapiPropertyDescriptor(napi_property_descriptor {
utf8name,
name,
method,
getter,
setter,
value,
attributes,
data: Box::into_raw(Box::new(data)) as _,
}))
}
}
impl Default for DescriptorValueBuilder {
fn default() -> Self {
Self::new()
}
}
impl<T: FromJsArgs, R: NapiValueT> Default for DescriptorMethodBuilder<T, R> {
fn default() -> Self {
Self::new()
}
}
impl<T: NapiValueT, R: NapiValueT> Default for DescriptorAccessorBuilder<T, R> {
fn default() -> Self {
Self::new()
}
}