509 lines
15 KiB
Rust
509 lines
15 KiB
Rust
use std::convert::{From, TryFrom};
|
|
use std::ffi::CString;
|
|
use std::ptr;
|
|
|
|
use crate::error::check_status;
|
|
use crate::{sys, Error, Result, Status};
|
|
|
|
#[cfg(feature = "serde-json")]
|
|
mod de;
|
|
#[cfg(feature = "serde-json")]
|
|
mod ser;
|
|
|
|
mod arraybuffer;
|
|
#[cfg(napi6)]
|
|
mod bigint;
|
|
mod boolean;
|
|
mod buffer;
|
|
#[cfg(napi5)]
|
|
mod date;
|
|
mod either;
|
|
mod escapable_handle_scope;
|
|
mod function;
|
|
mod global;
|
|
mod number;
|
|
mod object;
|
|
mod object_property;
|
|
mod string;
|
|
mod tagged_object;
|
|
mod undefined;
|
|
mod value;
|
|
mod value_ref;
|
|
mod value_type;
|
|
|
|
pub use arraybuffer::*;
|
|
#[cfg(napi6)]
|
|
pub use bigint::JsBigint;
|
|
pub use boolean::JsBoolean;
|
|
pub use buffer::*;
|
|
#[cfg(napi5)]
|
|
pub use date::*;
|
|
#[cfg(feature = "serde-json")]
|
|
pub(crate) use de::De;
|
|
pub use either::Either;
|
|
pub use escapable_handle_scope::EscapableHandleScope;
|
|
pub use function::JsFunction;
|
|
pub use global::*;
|
|
pub use number::JsNumber;
|
|
pub use object::JsObject;
|
|
pub use object_property::Property;
|
|
#[cfg(feature = "serde-json")]
|
|
pub(crate) use ser::Ser;
|
|
pub use string::*;
|
|
pub(crate) use tagged_object::TaggedObject;
|
|
pub use undefined::JsUndefined;
|
|
pub(crate) use value::Value;
|
|
pub use value_ref::Ref;
|
|
pub use value_type::ValueType;
|
|
|
|
// Value types
|
|
#[repr(transparent)]
|
|
#[derive(Debug)]
|
|
pub struct JsUnknown(pub(crate) Value);
|
|
|
|
#[repr(transparent)]
|
|
#[derive(Debug)]
|
|
pub struct JsNull(pub(crate) Value);
|
|
|
|
#[repr(transparent)]
|
|
#[derive(Debug)]
|
|
pub struct JsSymbol(pub(crate) Value);
|
|
|
|
#[repr(transparent)]
|
|
#[derive(Debug)]
|
|
pub struct JsExternal(pub(crate) Value);
|
|
|
|
#[inline]
|
|
pub(crate) fn type_of(env: sys::napi_env, raw_value: sys::napi_value) -> Result<ValueType> {
|
|
unsafe {
|
|
let mut value_type = sys::napi_valuetype::napi_undefined;
|
|
check_status(sys::napi_typeof(env, raw_value, &mut value_type))?;
|
|
Ok(ValueType::from(value_type))
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_napi_value_trait {
|
|
($js_value:ident, $value_type:ident) => {
|
|
impl NapiValue for $js_value {
|
|
fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<$js_value> {
|
|
let value_type = type_of(env, value)?;
|
|
if value_type != $value_type {
|
|
Err(Error::new(
|
|
Status::InvalidArg,
|
|
format!("expect {:?}, got: {:?}", $value_type, value_type),
|
|
))
|
|
} else {
|
|
Ok($js_value(Value {
|
|
env,
|
|
value,
|
|
value_type: $value_type,
|
|
}))
|
|
}
|
|
}
|
|
|
|
fn raw(&self) -> sys::napi_value {
|
|
self.0.value
|
|
}
|
|
|
|
fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> $js_value {
|
|
$js_value(Value {
|
|
env,
|
|
value,
|
|
value_type: $value_type,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl TryFrom<JsUnknown> for $js_value {
|
|
type Error = Error;
|
|
fn try_from(value: JsUnknown) -> Result<$js_value> {
|
|
$js_value::from_raw(value.0.env, value.0.value)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! impl_js_value_methods {
|
|
($js_value:ident) => {
|
|
impl $js_value {
|
|
#[inline]
|
|
pub fn into_unknown(self) -> JsUnknown {
|
|
JsUnknown::from_raw_unchecked(self.0.env, self.0.value)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn coerce_to_number(self) -> Result<JsNumber> {
|
|
let mut new_raw_value = ptr::null_mut();
|
|
let status =
|
|
unsafe { sys::napi_coerce_to_number(self.0.env, self.0.value, &mut new_raw_value) };
|
|
check_status(status)?;
|
|
Ok(JsNumber(Value {
|
|
env: self.0.env,
|
|
value: new_raw_value,
|
|
value_type: ValueType::Number,
|
|
}))
|
|
}
|
|
|
|
#[inline]
|
|
pub fn coerce_to_string(self) -> Result<JsString> {
|
|
let mut new_raw_value = ptr::null_mut();
|
|
let status =
|
|
unsafe { sys::napi_coerce_to_string(self.0.env, self.0.value, &mut new_raw_value) };
|
|
check_status(status)?;
|
|
Ok(JsString(Value {
|
|
env: self.0.env,
|
|
value: new_raw_value,
|
|
value_type: ValueType::String,
|
|
}))
|
|
}
|
|
|
|
#[inline]
|
|
pub fn coerce_to_object(self) -> Result<JsObject> {
|
|
let mut new_raw_value = ptr::null_mut();
|
|
let status =
|
|
unsafe { sys::napi_coerce_to_object(self.0.env, self.0.value, &mut new_raw_value) };
|
|
check_status(status)?;
|
|
Ok(JsObject(Value {
|
|
env: self.0.env,
|
|
value: new_raw_value,
|
|
value_type: ValueType::Object,
|
|
}))
|
|
}
|
|
|
|
#[inline]
|
|
#[cfg(napi5)]
|
|
pub fn is_date(&self) -> Result<bool> {
|
|
let mut is_date = true;
|
|
check_status(unsafe { sys::napi_is_date(self.0.env, self.0.value, &mut is_date) })?;
|
|
Ok(is_date)
|
|
}
|
|
|
|
pub fn is_promise(&self) -> Result<bool> {
|
|
let mut is_promise = true;
|
|
check_status(unsafe { sys::napi_is_promise(self.0.env, self.0.value, &mut is_promise) })?;
|
|
Ok(is_promise)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn is_error(&self) -> Result<bool> {
|
|
let mut result = false;
|
|
check_status(unsafe { sys::napi_is_error(self.0.env, self.0.value, &mut result) })?;
|
|
Ok(result)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn is_typedarray(&self) -> Result<bool> {
|
|
let mut result = false;
|
|
check_status(unsafe { sys::napi_is_typedarray(self.0.env, self.0.value, &mut result) })?;
|
|
Ok(result)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn is_dataview(&self) -> Result<bool> {
|
|
let mut result = false;
|
|
check_status(unsafe { sys::napi_is_dataview(self.0.env, self.0.value, &mut result) })?;
|
|
Ok(result)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn is_array(&self) -> Result<bool> {
|
|
let mut is_array = false;
|
|
check_status(unsafe { sys::napi_is_array(self.0.env, self.0.value, &mut is_array) })?;
|
|
Ok(is_array)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn is_buffer(&self) -> Result<bool> {
|
|
let mut is_buffer = false;
|
|
check_status(unsafe { sys::napi_is_buffer(self.0.env, self.0.value, &mut is_buffer) })?;
|
|
Ok(is_buffer)
|
|
}
|
|
|
|
#[inline]
|
|
pub fn instanceof<Constructor: NapiValue>(&self, constructor: Constructor) -> Result<bool> {
|
|
let mut result = false;
|
|
check_status(unsafe {
|
|
sys::napi_instanceof(self.0.env, self.0.value, constructor.raw(), &mut result)
|
|
})?;
|
|
Ok(result)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! impl_object_methods {
|
|
($js_value:ident) => {
|
|
impl $js_value {
|
|
pub fn set_property<V>(&mut self, key: JsString, value: V) -> Result<()>
|
|
where
|
|
V: NapiValue,
|
|
{
|
|
check_status(unsafe {
|
|
sys::napi_set_property(self.0.env, self.0.value, key.0.value, value.raw())
|
|
})
|
|
}
|
|
pub fn get_property<K, T>(&self, key: &K) -> Result<T>
|
|
where
|
|
K: NapiValue,
|
|
T: NapiValue,
|
|
{
|
|
let mut raw_value = ptr::null_mut();
|
|
check_status(unsafe {
|
|
sys::napi_get_property(self.0.env, self.0.value, key.raw(), &mut raw_value)
|
|
})?;
|
|
T::from_raw(self.0.env, raw_value)
|
|
}
|
|
pub fn set_named_property<T>(&mut self, name: &str, value: T) -> Result<()>
|
|
where
|
|
T: NapiValue,
|
|
{
|
|
let key = CString::new(name)?;
|
|
check_status(unsafe {
|
|
sys::napi_set_named_property(self.0.env, self.0.value, key.as_ptr(), value.raw())
|
|
})
|
|
}
|
|
pub fn get_named_property<T>(&self, name: &str) -> Result<T>
|
|
where
|
|
T: NapiValue,
|
|
{
|
|
let key = CString::new(name)?;
|
|
let mut raw_value = ptr::null_mut();
|
|
check_status(unsafe {
|
|
sys::napi_get_named_property(self.0.env, self.0.value, key.as_ptr(), &mut raw_value)
|
|
})?;
|
|
T::from_raw(self.0.env, raw_value)
|
|
}
|
|
pub fn has_named_property<S>(&self, name: S) -> Result<bool>
|
|
where
|
|
S: AsRef<str>,
|
|
{
|
|
let mut result = false;
|
|
let key = CString::new(name.as_ref())?;
|
|
check_status(unsafe {
|
|
sys::napi_has_named_property(self.0.env, self.0.value, key.as_ptr(), &mut result)
|
|
})?;
|
|
Ok(result)
|
|
}
|
|
pub fn delete_property<S>(&mut self, name: S) -> Result<bool>
|
|
where
|
|
S: NapiValue,
|
|
{
|
|
let mut result = false;
|
|
check_status(unsafe {
|
|
sys::napi_delete_property(self.0.env, self.0.value, name.raw(), &mut result)
|
|
})?;
|
|
Ok(result)
|
|
}
|
|
pub fn delete_named_property(&mut self, name: &str) -> Result<bool> {
|
|
let mut result = false;
|
|
let key_str = CString::new(name)?;
|
|
let mut js_key = ptr::null_mut();
|
|
check_status(unsafe {
|
|
sys::napi_create_string_utf8(self.0.env, key_str.as_ptr(), name.len() as _, &mut js_key)
|
|
})?;
|
|
check_status(unsafe {
|
|
sys::napi_delete_property(self.0.env, self.0.value, js_key, &mut result)
|
|
})?;
|
|
Ok(result)
|
|
}
|
|
pub fn has_own_property(&self, key: &str) -> Result<bool> {
|
|
let mut result = false;
|
|
let string = CString::new(key)?;
|
|
let mut js_key = ptr::null_mut();
|
|
check_status(unsafe {
|
|
sys::napi_create_string_utf8(self.0.env, string.as_ptr(), key.len() as _, &mut js_key)
|
|
})?;
|
|
check_status(unsafe {
|
|
sys::napi_has_own_property(self.0.env, self.0.value, js_key, &mut result)
|
|
})?;
|
|
Ok(result)
|
|
}
|
|
pub fn has_own_property_js<K>(&self, key: K) -> Result<bool>
|
|
where
|
|
K: NapiValue,
|
|
{
|
|
let mut result = false;
|
|
check_status(unsafe {
|
|
sys::napi_has_own_property(self.0.env, self.0.value, key.raw(), &mut result)
|
|
})?;
|
|
Ok(result)
|
|
}
|
|
pub fn has_property(&self, name: &str) -> Result<bool> {
|
|
let string = CString::new(name)?;
|
|
let mut js_key = ptr::null_mut();
|
|
let mut result = false;
|
|
check_status(unsafe {
|
|
sys::napi_create_string_utf8(self.0.env, string.as_ptr(), name.len() as _, &mut js_key)
|
|
})?;
|
|
check_status(unsafe {
|
|
sys::napi_has_property(self.0.env, self.0.value, js_key, &mut result)
|
|
})?;
|
|
Ok(result)
|
|
}
|
|
pub fn has_property_js<K>(&self, name: K) -> Result<bool>
|
|
where
|
|
K: NapiValue,
|
|
{
|
|
let mut result = false;
|
|
check_status(unsafe {
|
|
sys::napi_has_property(self.0.env, self.0.value, name.raw(), &mut result)
|
|
})?;
|
|
Ok(result)
|
|
}
|
|
pub fn get_property_names<T>(&self) -> Result<T>
|
|
where
|
|
T: NapiValue,
|
|
{
|
|
let mut raw_value = ptr::null_mut();
|
|
let status =
|
|
unsafe { sys::napi_get_property_names(self.0.env, self.0.value, &mut raw_value) };
|
|
check_status(status)?;
|
|
T::from_raw(self.0.env, raw_value)
|
|
}
|
|
pub fn get_prototype<T>(&self) -> Result<T>
|
|
where
|
|
T: NapiValue,
|
|
{
|
|
let mut result = ptr::null_mut();
|
|
check_status(unsafe { sys::napi_get_prototype(self.0.env, self.0.value, &mut result) })?;
|
|
T::from_raw(self.0.env, result)
|
|
}
|
|
pub fn set_element<T>(&mut self, index: u32, value: T) -> Result<()>
|
|
where
|
|
T: NapiValue,
|
|
{
|
|
check_status(unsafe { sys::napi_set_element(self.0.env, self.0.value, index, value.raw()) })
|
|
}
|
|
pub fn has_element(&self, index: u32) -> Result<bool> {
|
|
let mut result = false;
|
|
check_status(unsafe {
|
|
sys::napi_has_element(self.0.env, self.0.value, index, &mut result)
|
|
})?;
|
|
Ok(result)
|
|
}
|
|
pub fn delete_element(&mut self, index: u32) -> Result<bool> {
|
|
let mut result = false;
|
|
check_status(unsafe {
|
|
sys::napi_delete_element(self.0.env, self.0.value, index, &mut result)
|
|
})?;
|
|
Ok(result)
|
|
}
|
|
pub fn get_element<T>(&self, index: u32) -> Result<T>
|
|
where
|
|
T: NapiValue,
|
|
{
|
|
let mut raw_value = ptr::null_mut();
|
|
check_status(unsafe {
|
|
sys::napi_get_element(self.0.env, self.0.value, index, &mut raw_value)
|
|
})?;
|
|
T::from_raw(self.0.env, raw_value)
|
|
}
|
|
pub fn define_properties(&mut self, properties: &[Property]) -> Result<()> {
|
|
check_status(unsafe {
|
|
sys::napi_define_properties(
|
|
self.0.env,
|
|
self.0.value,
|
|
properties.len() as _,
|
|
properties
|
|
.iter()
|
|
.map(|property| property.raw())
|
|
.collect::<Vec<sys::napi_property_descriptor>>()
|
|
.as_ptr(),
|
|
)
|
|
})
|
|
}
|
|
pub fn get_array_length(&self) -> Result<u32> {
|
|
if self.is_array()? != true {
|
|
return Err(Error::new(
|
|
Status::ArrayExpected,
|
|
"Object is not array".to_owned(),
|
|
));
|
|
}
|
|
self.get_array_length_unchecked()
|
|
}
|
|
#[inline]
|
|
pub fn get_array_length_unchecked(&self) -> Result<u32> {
|
|
let mut length: u32 = 0;
|
|
check_status(unsafe { sys::napi_get_array_length(self.0.env, self.raw(), &mut length) })?;
|
|
Ok(length)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
pub trait NapiValue: Sized {
|
|
fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self>;
|
|
|
|
fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self;
|
|
|
|
fn raw(&self) -> sys::napi_value;
|
|
}
|
|
|
|
impl_js_value_methods!(JsUnknown);
|
|
impl_js_value_methods!(JsUndefined);
|
|
impl_js_value_methods!(JsNull);
|
|
impl_js_value_methods!(JsBoolean);
|
|
impl_js_value_methods!(JsArrayBuffer);
|
|
impl_js_value_methods!(JsBuffer);
|
|
impl_js_value_methods!(JsNumber);
|
|
impl_js_value_methods!(JsString);
|
|
impl_js_value_methods!(JsObject);
|
|
impl_js_value_methods!(JsGlobal);
|
|
#[cfg(napi5)]
|
|
impl_js_value_methods!(JsDate);
|
|
impl_js_value_methods!(JsFunction);
|
|
impl_js_value_methods!(JsExternal);
|
|
impl_js_value_methods!(JsSymbol);
|
|
impl_js_value_methods!(JsTimeout);
|
|
|
|
impl_object_methods!(JsObject);
|
|
impl_object_methods!(JsBuffer);
|
|
impl_object_methods!(JsArrayBuffer);
|
|
impl_object_methods!(JsGlobal);
|
|
|
|
use ValueType::*;
|
|
|
|
impl_napi_value_trait!(JsUndefined, Undefined);
|
|
impl_napi_value_trait!(JsNull, Null);
|
|
impl_napi_value_trait!(JsBoolean, Boolean);
|
|
impl_napi_value_trait!(JsBuffer, Object);
|
|
impl_napi_value_trait!(JsArrayBuffer, Object);
|
|
impl_napi_value_trait!(JsNumber, Number);
|
|
impl_napi_value_trait!(JsString, String);
|
|
impl_napi_value_trait!(JsObject, Object);
|
|
impl_napi_value_trait!(JsGlobal, Object);
|
|
#[cfg(napi5)]
|
|
impl_napi_value_trait!(JsDate, Object);
|
|
impl_napi_value_trait!(JsTimeout, Object);
|
|
impl_napi_value_trait!(JsFunction, Function);
|
|
impl_napi_value_trait!(JsExternal, External);
|
|
impl_napi_value_trait!(JsSymbol, Symbol);
|
|
|
|
impl NapiValue for JsUnknown {
|
|
fn from_raw(env: sys::napi_env, value: sys::napi_value) -> Result<Self> {
|
|
Ok(JsUnknown(Value {
|
|
env,
|
|
value,
|
|
value_type: Unknown,
|
|
}))
|
|
}
|
|
|
|
fn from_raw_unchecked(env: sys::napi_env, value: sys::napi_value) -> Self {
|
|
JsUnknown(Value {
|
|
env,
|
|
value,
|
|
value_type: Unknown,
|
|
})
|
|
}
|
|
|
|
fn raw(&self) -> sys::napi_value {
|
|
self.0.value
|
|
}
|
|
}
|
|
|
|
impl JsUnknown {
|
|
#[inline]
|
|
pub fn get_type(&self) -> Result<ValueType> {
|
|
type_of(self.0.env, self.0.value)
|
|
}
|
|
}
|