Skip to content

Commit 8b0ccc6

Browse files
committed
feat: validate arguments and pack them into structures
1 parent d1d4a4a commit 8b0ccc6

File tree

17 files changed

+260
-40
lines changed

17 files changed

+260
-40
lines changed

example/example.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22

33
const addon = require('./build/Release/example.node');
44

5-
addon.initialize();
5+
addon.hello(undefined);
6+
console.log(addon.add(1, 2));

example/src/binding.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#include <node_api.h>
22

33
#define FUNCTIONS_MAP(V) \
4-
V(initialize, example_initialize)
4+
V(hello, example_hello) \
5+
V(add, example_add)
56

67
#define V(name, func) \
78
extern napi_value func(napi_env env, napi_callback_info info);

example/src/lib.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,30 @@ extern crate napi;
33
#[macro_use]
44
extern crate napi_derive;
55

6-
use napi::{NapiArgs, NapiEnv, NapiResult, NapiUndefined};
6+
use napi::{NapiArgs, NapiEnv, NapiNumber, NapiResult, NapiUndefined};
77

88
#[derive(NapiArgs)]
9-
struct InitializeArgs();
9+
struct HelloArgs<'a>(NapiUndefined<'a>);
1010

11-
fn initialize(
12-
env: &NapiEnv,
13-
_args: InitializeArgs,
14-
) -> NapiResult<NapiUndefined> {
11+
fn hello<'a>(
12+
env: &'a NapiEnv,
13+
_: HelloArgs<'a>,
14+
) -> NapiResult<NapiUndefined<'a>> {
1515
println!("Hello from the Rust land!");
1616
NapiUndefined::new(env)
1717
}
1818

19-
napi_callback!(example_initialize, initialize);
19+
#[derive(NapiArgs)]
20+
struct AddArgs<'a> {
21+
first: NapiNumber<'a>,
22+
second: NapiNumber<'a>,
23+
}
24+
25+
fn add<'a>(env: &'a NapiEnv, args: AddArgs<'a>) -> NapiResult<NapiNumber<'a>> {
26+
let first = args.first.to_i32()?;
27+
let second = args.second.to_i32()?;
28+
NapiNumber::from_i32(env, first + second)
29+
}
30+
31+
napi_callback!(example_hello, hello);
32+
napi_callback!(example_add, add);

napi-derive/src/lib.rs

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,62 @@ fn impl_napi_args(
2626
_ => return Err("NapiArgs can only be derived for structs"),
2727
};
2828

29-
let fields = match variant_data {
30-
&syn::VariantData::Tuple(ref fields) => fields,
31-
_ => return Err("NapiArgs can only be derived for tuple-structs"),
29+
let (init_list, count) = match *variant_data {
30+
syn::VariantData::Struct(ref fields) => {
31+
let inner = fields
32+
.iter()
33+
.enumerate()
34+
.map(|(idx, field)| {
35+
let ident = field.clone().ident.unwrap();
36+
quote! {
37+
#ident: <_ as NapiValue>::from_sys_checked(
38+
env,
39+
argv[#idx],
40+
)?
41+
}
42+
})
43+
.collect::<Vec<_>>();
44+
45+
let outer = quote! {
46+
{ #(#inner),* }
47+
};
48+
49+
(Some(outer), fields.len())
50+
}
51+
52+
syn::VariantData::Tuple(ref fields) => {
53+
let inner = (0..fields.len())
54+
.map(|idx| {
55+
quote! {
56+
<_ as NapiValue>::from_sys_checked(env, argv[#idx])?
57+
}
58+
})
59+
.collect::<Vec<_>>();
60+
61+
let outer = quote! {
62+
( #(#inner),* )
63+
};
64+
65+
(Some(outer), fields.len())
66+
}
67+
68+
syn::VariantData::Unit => (None, 0),
69+
};
70+
71+
let construct = if let Some(init_list) = init_list {
72+
quote! { #name #init_list }
73+
} else {
74+
quote! { #name }
3275
};
33-
let count = fields.len();
3476

3577
Ok(quote! {
36-
impl NapiArgs for #name {
78+
impl<'env> NapiArgs<'env> for #name<'env> {
3779
fn from_cb_info(
38-
env: &::napi::NapiEnv,
80+
env: &'env ::napi::NapiEnv,
3981
cb_info: ::napi::sys::napi_callback_info,
4082
) -> ::napi::NapiResult<Self> {
4183
use ::napi::sys;
42-
use ::napi::{NapiError, NapiString};
84+
use ::napi::{NapiError, NapiString, NapiValue};
4385

4486
use ::std::ptr;
4587

@@ -66,7 +108,7 @@ fn impl_napi_args(
66108
return Err(NapiError::type_error(env, &message));
67109
}
68110

69-
Ok(#name())
111+
Ok(#construct)
70112
}
71113
}
72114
})

napi/src/args.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use env::NapiEnv;
22
use result::NapiResult;
33
use sys;
44

5-
pub trait NapiArgs: Sized {
5+
pub trait NapiArgs<'env>: Sized {
66
fn from_cb_info(
7-
env: &NapiEnv,
7+
env: &'env NapiEnv,
88
cb_info: sys::napi_callback_info,
99
) -> NapiResult<Self>;
1010
}

napi/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ pub use args::NapiArgs;
99
pub use env::NapiEnv;
1010
pub use result::{NapiError, NapiErrorKind, NapiResult};
1111
pub use value::{AsNapiObject, NapiAny, NapiArray, NapiArrayBuffer,
12-
NapiBoolean, NapiBuffer, NapiNull, NapiObject, NapiString,
13-
NapiUndefined, NapiValue, NapiValueType};
12+
NapiBoolean, NapiBuffer, NapiNull, NapiNumber, NapiObject,
13+
NapiString, NapiUndefined, NapiValue, NapiValueType};
1414

1515
pub mod sys {
1616
pub use napi_sys::*;

napi/src/value/any.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,11 @@ impl<'env> NapiValue<'env> for NapiAny<'env> {
122122
fn env(&self) -> &'env NapiEnv {
123123
self.env
124124
}
125+
126+
fn from_sys_checked(
127+
env: &'env NapiEnv,
128+
value: sys::napi_value,
129+
) -> NapiResult<Self> {
130+
Ok(Self { env, value })
131+
}
125132
}

napi/src/value/array.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use std::ptr;
22

33
use env::NapiEnv;
4-
use result::NapiResult;
4+
use result::{NapiError, NapiResult};
55
use sys;
66

7-
use super::{AsNapiObject, NapiAny, NapiValue, NapiValueInternal};
7+
use super::{AsNapiObject, NapiAny, NapiString, NapiValue, NapiValueInternal};
88

99
#[derive(Clone, Copy, Debug)]
1010
pub struct NapiArray<'env> {
@@ -73,6 +73,18 @@ impl<'env> NapiValue<'env> for NapiArray<'env> {
7373
fn env(&self) -> &'env NapiEnv {
7474
self.env
7575
}
76+
77+
fn from_sys_checked(
78+
env: &'env NapiEnv,
79+
value: sys::napi_value,
80+
) -> NapiResult<Self> {
81+
if !NapiAny::with_value(env, value).is_array()? {
82+
let message = NapiString::from_str(env, "Array expected")?;
83+
return Err(NapiError::type_error(env, &message));
84+
}
85+
86+
Ok(Self { env, value })
87+
}
7688
}
7789

7890
impl<'env> NapiValueInternal<'env> for NapiArray<'env> {

napi/src/value/array_buffer.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ use std::ptr;
22
use std::slice;
33

44
use env::NapiEnv;
5-
use result::NapiResult;
5+
use result::{NapiError, NapiResult};
66
use sys;
77

8-
use super::{AsNapiObject, NapiValue};
8+
use super::{AsNapiObject, NapiAny, NapiString, NapiValue};
99

1010
#[derive(Debug)]
1111
pub struct NapiArrayBuffer<'env, 'buf> {
@@ -52,6 +52,34 @@ impl<'env, 'buf> NapiValue<'env> for NapiArrayBuffer<'env, 'buf> {
5252
fn env(&self) -> &'env NapiEnv {
5353
self.env
5454
}
55+
56+
fn from_sys_checked(
57+
env: &'env NapiEnv,
58+
value: sys::napi_value,
59+
) -> NapiResult<Self> {
60+
if !NapiAny::with_value(env, value).is_arraybuffer()? {
61+
let message = NapiString::from_str(env, "ArrayBuffer expected")?;
62+
return Err(NapiError::type_error(env, &message));
63+
}
64+
65+
let mut data = ptr::null_mut();
66+
let mut len = 0;
67+
68+
env.handle_status(unsafe {
69+
sys::napi_get_arraybuffer_info(
70+
env.as_sys_env(),
71+
value,
72+
&mut data,
73+
&mut len,
74+
)
75+
})?;
76+
77+
Ok(Self {
78+
env,
79+
value,
80+
data: unsafe { slice::from_raw_parts_mut(data as *mut u8, len) },
81+
})
82+
}
5583
}
5684

5785
impl<'env, 'buf> AsNapiObject<'env> for NapiArrayBuffer<'env, 'buf> {}

napi/src/value/boolean.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use std::ptr;
22

33
use env::NapiEnv;
4-
use result::NapiResult;
4+
use result::{NapiError, NapiResult};
55
use sys;
66

7-
use super::{NapiValue, NapiValueInternal};
7+
use super::{NapiAny, NapiString, NapiValue, NapiValueInternal, NapiValueType};
88

99
#[derive(Clone, Copy, Debug)]
1010
pub struct NapiBoolean<'env> {
@@ -56,6 +56,20 @@ impl<'env> NapiValue<'env> for NapiBoolean<'env> {
5656
fn env(&self) -> &'env NapiEnv {
5757
self.env
5858
}
59+
60+
fn from_sys_checked(
61+
env: &'env NapiEnv,
62+
value: sys::napi_value,
63+
) -> NapiResult<Self> {
64+
if NapiAny::with_value(env, value).value_type()?
65+
!= NapiValueType::Boolean
66+
{
67+
let message = NapiString::from_str(env, "Boolean expected")?;
68+
return Err(NapiError::type_error(env, &message));
69+
}
70+
71+
Ok(Self { env, value })
72+
}
5973
}
6074

6175
impl<'env> NapiValueInternal<'env> for NapiBoolean<'env> {

0 commit comments

Comments
 (0)