node_api_macro/
lib.rs

1use quote::quote;
2use syn::spanned::Spanned;
3
4#[proc_macro]
5pub fn init(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
6	init_impl(input.into())
7		.unwrap_or_else(|e| e.to_compile_error())
8		.into()
9}
10
11fn init_impl(input: proc_macro2::TokenStream) -> syn::Result<proc_macro2::TokenStream> {
12	let input: syn::Ident = syn::parse2(input)?;
13	let code = quote! {
14		#[no_mangle]
15		pub unsafe extern "C" fn napi_register_module_v1(env: node_api::sys::napi_env, exports: node_api::sys::napi_value) -> node_api::sys::napi_value {
16			let env = node_api::Env::from_raw(env);
17			let exports = node_api::Value::from_raw(env, exports);
18			let result = std::panic::catch_unwind(|| #input(env, exports));
19			let result = match result {
20				Ok(result) => result,
21				Err(panic_info) => {
22					env.throw_error("A panic occurred.");
23					return std::ptr::null_mut();
24				},
25			};
26			let exports = match result {
27				Ok(exports) => exports,
28				Err(error) => {
29					if !env.is_exception_pending() {
30						env.throw_error(&format!("{}", error));
31					}
32					return std::ptr::null_mut();
33				}
34			};
35			exports.raw()
36		}
37	};
38	Ok(code)
39}
40
41#[proc_macro_attribute]
42pub fn function(
43	_attr: proc_macro::TokenStream,
44	input: proc_macro::TokenStream,
45) -> proc_macro::TokenStream {
46	function_impl(input.into())
47		.unwrap_or_else(|e| e.to_compile_error())
48		.into()
49}
50
51fn function_impl(input: proc_macro2::TokenStream) -> syn::Result<proc_macro2::TokenStream> {
52	let input: syn::ItemFn = syn::parse2(input)?;
53	let visibility = &input.vis;
54	let ident = &input.sig.ident;
55	let impl_inputs = input.sig.inputs.iter().skip(1);
56	let impl_output = &input.sig.output;
57	let impl_block = &input.block;
58	let args = input
59		.sig
60		.inputs
61		.iter()
62		.skip(1)
63		.map(|input| {
64			let input = match input {
65				syn::FnArg::Typed(arg) => arg,
66				syn::FnArg::Receiver(_) => {
67					return Err(syn::Error::new(
68						input.span(),
69						"receiver arg is not allowed here",
70					))
71				}
72			};
73			let ident = match &*input.pat {
74				syn::Pat::Ident(pat_ident) => &pat_ident.ident,
75				_ => return Err(syn::Error::new(input.pat.span(), "invalid pattern")),
76			};
77			Ok(ident)
78		})
79		.collect::<syn::Result<Vec<_>>>()?;
80	let args_count = args.len();
81	let from_node_api_statements = args
82		.iter()
83		.enumerate()
84		.map(|(i, ident)| {
85			let code = quote! {
86				let #ident = argv[#i];
87				let #ident = node_api::Value::from_raw(env, #ident);
88				let #ident = node_api::FromNodeAPI::from_node_api(#ident)?;
89			};
90			Ok(code)
91		})
92		.collect::<syn::Result<Vec<_>>>()?;
93	let code = quote! {
94		#visibility unsafe extern "C" fn #ident(env: node_api::sys::napi_env, info: node_api::sys::napi_callback_info) -> node_api::sys::napi_value {
95			fn function_impl<'a>(env: node_api::Env<'a>, #(#impl_inputs),*) #impl_output #impl_block
96			let env = node_api::Env::from_raw(env);
97			let result = std::panic::catch_unwind(|| -> node_api::Result<_> {
98				let mut argc = #args_count;
99				let mut argv: [node_api::sys::napi_value; #args_count] = [std::ptr::null_mut(); #args_count];
100				let status = node_api::sys::napi_get_cb_info(
101					env.raw(),
102					info,
103					&mut argc as *mut usize,
104					argv.as_mut_ptr() as *mut node_api::sys::napi_value,
105					std::ptr::null_mut(),
106					std::ptr::null_mut()
107				);
108				if status != node_api::sys::napi_status::napi_ok {
109					return Err(node_api::Error::from_last_node_api_error(env.raw(), status).into());
110				}
111				if argc != #args_count {
112					return Err(node_api::Error::message("invalid number of arguments").into());
113				}
114				#(#from_node_api_statements)*
115				let output = function_impl(env, #(#args),*).map_err(|error| node_api::Error::message(error.to_string()))?;
116				let output = node_api::IntoNodeApi::into_node_api(output, env)?;
117				Ok(output)
118			});
119			let result = match result {
120				Ok(result) => result,
121				Err(_) => {
122					env.throw_error("A panic occurred.");
123					return std::ptr::null_mut();
124				},
125			};
126			let output = match result {
127				Ok(output) => output,
128				Err(error) => {
129					if !env.is_exception_pending() {
130						env.throw_error(&format!("{}", error));
131					}
132					return std::ptr::null_mut();
133				}
134			};
135			output.raw()
136		}
137	};
138	Ok(code)
139}