use std::{marker::PhantomData, ptr};
use crate::{
bindgen_prelude::{
sys, External, ExternalRef, FromNapiValue, Result, Status, TypeName, ValidateNapiValue,
},
check_status, Error, JsValue, Value, ValueType,
};
pub struct JsExternal<'env>(pub(crate) Value, PhantomData<&'env ()>);
impl<'env> TypeName for JsExternal<'env> {
fn type_name() -> &'static str {
"External"
}
fn value_type() -> ValueType {
ValueType::External
}
}
impl<'env> ValidateNapiValue for JsExternal<'env> {}
impl<'env> FromNapiValue for JsExternal<'env> {
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
Ok(Self(
Value {
env,
value: napi_val,
value_type: ValueType::External,
},
PhantomData,
))
}
}
impl<'env> JsValue<'env> for JsExternal<'env> {
fn value(&self) -> Value {
self.0
}
}
impl<'env> JsExternal<'env> {
pub fn get_value<T: 'static>(&self) -> Result<&mut T> {
self.get_static_value::<T>().map(|ext| ext.as_mut())
}
pub fn create_ref<T: 'static>(&self) -> Result<ExternalRef<T>> {
let mut ref_ = ptr::null_mut();
let external = self.get_static_value()?;
check_status!(
unsafe { sys::napi_create_reference(self.0.env, self.0.value, 1, &mut ref_) },
"Failed to create reference on external value"
)?;
Ok(ExternalRef {
obj: external,
raw: ref_,
env: self.0.env,
})
}
#[inline]
fn get_static_value<T: 'static>(&self) -> Result<&'static mut External<T>> {
let mut unknown_tagged_object = ptr::null_mut();
check_status!(
unsafe { sys::napi_get_value_external(self.0.env, self.0.value, &mut unknown_tagged_object) },
"Failed to get external value"
)?;
match unsafe { External::from_raw_impl(unknown_tagged_object) } {
Some(external) => Ok(external),
None => Err(Error::new(
Status::InvalidArg,
format!(
"<{}> on `External` is not the type of wrapped object",
std::any::type_name::<T>()
),
)),
}
}
}