feat(napi): support into_instance in class struct
This commit is contained in:
parent
717f96acfc
commit
c1e07b3c12
9 changed files with 127 additions and 1 deletions
|
@ -263,6 +263,21 @@ impl NapiStruct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn into_instance(self, env: napi::Env) -> napi::Result<napi::bindgen_prelude::ClassInstance<#name>> {
|
||||
if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
|
||||
unsafe {
|
||||
let wrapped_value = Box::leak(Box::new(self));
|
||||
let instance_value = #name::new_instance(env.raw(), wrapped_value as *mut _ as *mut std::ffi::c_void, ctor_ref)?;
|
||||
|
||||
Ok(napi::bindgen_prelude::ClassInstance::<#name>::new(instance_value, wrapped_value))
|
||||
}
|
||||
} else {
|
||||
Err(napi::bindgen_prelude::Error::new(
|
||||
napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn new_instance(
|
||||
env: napi::sys::napi_env,
|
||||
wrapped_value: *mut std::ffi::c_void,
|
||||
|
|
|
@ -175,6 +175,7 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
|||
("Buffer", "Buffer"),
|
||||
("Vec", "Array<{}>"),
|
||||
("Result", "Error | {}"),
|
||||
("ClassInstance", "{}"),
|
||||
("Either", "{} | {}"),
|
||||
("Either3", "{} | {} | {}"),
|
||||
("Either4", "{} | {} | {} | {}"),
|
||||
|
|
|
@ -8,6 +8,7 @@ mod arraybuffer;
|
|||
mod bigint;
|
||||
mod boolean;
|
||||
mod buffer;
|
||||
mod class;
|
||||
#[cfg(all(feature = "chrono_date", feature = "napi5"))]
|
||||
mod date;
|
||||
mod either;
|
||||
|
@ -34,6 +35,7 @@ pub use arraybuffer::*;
|
|||
#[cfg(feature = "napi6")]
|
||||
pub use bigint::*;
|
||||
pub use buffer::*;
|
||||
pub use class::*;
|
||||
pub use either::*;
|
||||
pub use external::*;
|
||||
#[cfg(feature = "napi4")]
|
||||
|
|
65
crates/napi/src/bindgen_runtime/js_values/class.rs
Normal file
65
crates/napi/src/bindgen_runtime/js_values/class.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
use std::any::type_name;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ptr;
|
||||
|
||||
use crate::{bindgen_runtime::FromNapiValue, check_status, sys, NapiRaw};
|
||||
|
||||
pub struct ClassInstance<T: 'static> {
|
||||
pub value: sys::napi_value,
|
||||
inner: &'static mut T,
|
||||
}
|
||||
|
||||
impl<T: 'static> ClassInstance<T> {
|
||||
#[doc(hidden)]
|
||||
pub fn new(value: sys::napi_value, inner: &'static mut T) -> Self {
|
||||
Self { value, inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> NapiRaw for ClassInstance<T> {
|
||||
unsafe fn raw(&self) -> sys::napi_value {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> FromNapiValue for ClassInstance<T> {
|
||||
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
|
||||
let mut value = ptr::null_mut();
|
||||
check_status!(
|
||||
unsafe { sys::napi_unwrap(env, napi_val, &mut value) },
|
||||
"Unwrap value [{}] from class failed",
|
||||
type_name::<T>(),
|
||||
)?;
|
||||
let value = unsafe { Box::from_raw(value as *mut T) };
|
||||
Ok(Self {
|
||||
value: napi_val,
|
||||
inner: Box::leak(value),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Deref for ClassInstance<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> DerefMut for ClassInstance<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> AsRef<T> for ClassInstance<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> AsMut<T> for ClassInstance<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
self.inner
|
||||
}
|
||||
}
|
|
@ -41,6 +41,11 @@ Generated by [AVA](https://avajs.dev).
|
|||
export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void␊
|
||||
export function returnJsFunction(): (...args: any[]) => any␊
|
||||
export function callbackReturnPromise<T>(functionInput: () => T | Promise<T>, callback: (err: Error | null, result: T) => void): T | Promise<T>␊
|
||||
export interface ObjectFieldClassInstance {␊
|
||||
bird: Bird␊
|
||||
}␊
|
||||
export function createObjectWithClassField(): ObjectFieldClassInstance␊
|
||||
export function receiveObjectWithClassField(object: ObjectFieldClassInstance): Bird␊
|
||||
export function dateToNumber(input: Date): number␊
|
||||
export function chronoDateToMillis(input: Date): number␊
|
||||
export function chronoDateAdd1Minute(input: Date): Date␊
|
||||
|
|
Binary file not shown.
|
@ -97,6 +97,8 @@ import {
|
|||
eitherFromOption,
|
||||
overrideIndividualArgOnFunction,
|
||||
overrideIndividualArgOnFunctionWithCbArg,
|
||||
createObjectWithClassField,
|
||||
receiveObjectWithClassField,
|
||||
} from '../'
|
||||
|
||||
test('export const', (t) => {
|
||||
|
@ -206,6 +208,12 @@ test('class Factory return Result', (t) => {
|
|||
t.is(c.method(), 'not empty')
|
||||
})
|
||||
|
||||
test('class in object field', (t) => {
|
||||
const obj = createObjectWithClassField()
|
||||
t.is(obj.bird.name, 'Carolyn')
|
||||
t.is(receiveObjectWithClassField(obj), obj.bird)
|
||||
})
|
||||
|
||||
test('should be able to create object reference and shared reference', (t) => {
|
||||
const repo = new JsRepo('.')
|
||||
t.is(repo.remote().name(), 'origin')
|
||||
|
|
5
examples/napi/index.d.ts
vendored
5
examples/napi/index.d.ts
vendored
|
@ -31,6 +31,11 @@ export function optionOnly(callback: (arg0?: string | undefined | null) => void)
|
|||
export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void
|
||||
export function returnJsFunction(): (...args: any[]) => any
|
||||
export function callbackReturnPromise<T>(functionInput: () => T | Promise<T>, callback: (err: Error | null, result: T) => void): T | Promise<T>
|
||||
export interface ObjectFieldClassInstance {
|
||||
bird: Bird
|
||||
}
|
||||
export function createObjectWithClassField(): ObjectFieldClassInstance
|
||||
export function receiveObjectWithClassField(object: ObjectFieldClassInstance): Bird
|
||||
export function dateToNumber(input: Date): number
|
||||
export function chronoDateToMillis(input: Date): number
|
||||
export function chronoDateAdd1Minute(input: Date): Date
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use napi::{bindgen_prelude::Buffer, Result};
|
||||
use napi::{
|
||||
bindgen_prelude::{Buffer, ClassInstance},
|
||||
Env, Result,
|
||||
};
|
||||
|
||||
use crate::r#enum::Kind;
|
||||
|
||||
|
@ -305,3 +308,25 @@ impl Optional {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[napi(object)]
|
||||
pub struct ObjectFieldClassInstance {
|
||||
pub bird: ClassInstance<Bird>,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn create_object_with_class_field(env: Env) -> Result<ObjectFieldClassInstance> {
|
||||
Ok(ObjectFieldClassInstance {
|
||||
bird: Bird {
|
||||
name: "Carolyn".to_owned(),
|
||||
}
|
||||
.into_instance(env)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn receive_object_with_class_field(
|
||||
object: ObjectFieldClassInstance,
|
||||
) -> Result<ClassInstance<Bird>> {
|
||||
Ok(object.bird)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue