napi_derive_backend/codegen/
enum.rs1use proc_macro2::{Ident, Literal, Span, TokenStream};
2use quote::ToTokens;
3
4use crate::{codegen::js_mod_to_token_stream, BindgenResult, NapiEnum, TryToTokens};
5
6impl TryToTokens for NapiEnum {
7 fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
8 let register = self.gen_module_register();
9 let napi_value_conversion = self.gen_napi_value_map_impl();
10
11 (quote! {
12 #napi_value_conversion
13 #register
14 })
15 .to_tokens(tokens);
16
17 Ok(())
18 }
19}
20
21impl NapiEnum {
22 fn gen_napi_value_map_impl(&self) -> TokenStream {
23 let name = &self.name;
24 let name_str = self.name.to_string();
25 let mut from_napi_branches = vec![];
26 let mut to_napi_branches = vec![];
27
28 self.variants.iter().for_each(|v| {
29 let val: Literal = (&v.val).into();
30 let v_name = &v.name;
31
32 from_napi_branches.push(quote! { #val => Ok(#name::#v_name) });
33 to_napi_branches.push(quote! { #name::#v_name => #val });
34 });
35
36 let validate_type = if self.is_string_enum {
37 quote! { napi::bindgen_prelude::ValueType::String }
38 } else {
39 quote! { napi::bindgen_prelude::ValueType::Number }
40 };
41
42 let from_napi_value = if self.variants.is_empty() {
43 quote! {
44 impl napi::bindgen_prelude::FromNapiValue for #name {
45 unsafe fn from_napi_value(
46 env: napi::bindgen_prelude::sys::napi_env,
47 napi_val: napi::bindgen_prelude::sys::napi_value
48 ) -> napi::bindgen_prelude::Result<Self> {
49 Err(napi::bindgen_prelude::error!(
50 napi::bindgen_prelude::Status::InvalidArg,
51 "enum `{}` has no variants",
52 #name_str
53 ))
54 }
55 }
56 }
57 } else {
58 let from_napi_value = if self.is_string_enum {
59 quote! {
60 let val: String = napi::bindgen_prelude::FromNapiValue::from_napi_value(env, napi_val)
61 }
62 } else {
63 quote! {
64 let val = napi::bindgen_prelude::FromNapiValue::from_napi_value(env, napi_val)
65 }
66 };
67 let match_val = if self.is_string_enum {
68 quote! { val.as_str() }
69 } else {
70 quote! { val }
71 };
72 quote! {
73 impl napi::bindgen_prelude::FromNapiValue for #name {
74 unsafe fn from_napi_value(
75 env: napi::bindgen_prelude::sys::napi_env,
76 napi_val: napi::bindgen_prelude::sys::napi_value
77 ) -> napi::bindgen_prelude::Result<Self> {
78 #from_napi_value.map_err(|e| {
79 napi::bindgen_prelude::error!(
80 e.status,
81 "Failed to convert napi value into enum `{}`. {}",
82 #name_str,
83 e,
84 )
85 })?;
86
87 match #match_val {
88 #(#from_napi_branches,)*
89 _ => {
90 Err(napi::bindgen_prelude::error!(
91 napi::bindgen_prelude::Status::InvalidArg,
92 "value `{:?}` does not match any variant of enum `{}`",
93 val,
94 #name_str
95 ))
96 }
97 }
98 }
99 }
100 }
101 };
102
103 let to_napi_value = if self.variants.is_empty() {
104 quote! {
105 impl napi::bindgen_prelude::ToNapiValue for #name {
106 unsafe fn to_napi_value(
107 env: napi::bindgen_prelude::sys::napi_env,
108 val: Self
109 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
110 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, ())
111 }
112 }
113
114 impl napi::bindgen_prelude::ToNapiValue for &#name {
115 unsafe fn to_napi_value(
116 env: napi::bindgen_prelude::sys::napi_env,
117 val: Self
118 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
119 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, ())
120 }
121 }
122
123 impl napi::bindgen_prelude::ToNapiValue for &mut #name {
124 unsafe fn to_napi_value(
125 env: napi::bindgen_prelude::sys::napi_env,
126 val: Self
127 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
128 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, ())
129 }
130 }
131 }
132 } else {
133 quote! {
134 impl napi::bindgen_prelude::ToNapiValue for #name {
135 unsafe fn to_napi_value(
136 env: napi::bindgen_prelude::sys::napi_env,
137 val: Self
138 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
139 let val = match val {
140 #(#to_napi_branches,)*
141 };
142
143 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, val)
144 }
145 }
146
147 impl napi::bindgen_prelude::ToNapiValue for &#name {
148 unsafe fn to_napi_value(
149 env: napi::bindgen_prelude::sys::napi_env,
150 val: Self
151 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
152 let val = match val {
153 #(#to_napi_branches,)*
154 };
155
156 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, val)
157 }
158 }
159
160 impl napi::bindgen_prelude::ToNapiValue for &mut #name {
161 unsafe fn to_napi_value(
162 env: napi::bindgen_prelude::sys::napi_env,
163 val: Self
164 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
165 let val = match val {
166 #(#to_napi_branches,)*
167 };
168
169 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, val)
170 }
171 }
172 }
173 };
174
175 quote! {
176 impl napi::bindgen_prelude::TypeName for #name {
177 fn type_name() -> &'static str {
178 #name_str
179 }
180
181 fn value_type() -> napi::ValueType {
182 napi::ValueType::Object
183 }
184 }
185
186 impl napi::bindgen_prelude::ValidateNapiValue for #name {
187 unsafe fn validate(
188 env: napi::bindgen_prelude::sys::napi_env,
189 napi_val: napi::bindgen_prelude::sys::napi_value
190 ) -> napi::bindgen_prelude::Result<napi::sys::napi_value> {
191 napi::bindgen_prelude::assert_type_of!(env, napi_val, #validate_type)?;
192 Ok(std::ptr::null_mut())
193 }
194 }
195
196 #from_napi_value
197
198 #to_napi_value
199 }
200 }
201
202 fn gen_module_register(&self) -> TokenStream {
203 let name_str = self.name.to_string();
204 let js_name_lit = Literal::string(&format!("{}\0", &self.js_name));
205 let register_name = &self.register_name;
206
207 let mut define_properties = vec![];
208
209 for variant in self.variants.iter() {
210 let name_lit = Literal::string(&format!("{}\0", variant.name));
211 let val_lit: Literal = (&variant.val).into();
212
213 define_properties.push(quote! {
214 {
215 let name = std::ffi::CStr::from_bytes_with_nul_unchecked(#name_lit.as_bytes());
216 napi::bindgen_prelude::check_status!(
217 napi::bindgen_prelude::sys::napi_set_named_property(
218 env,
219 obj_ptr, name.as_ptr(),
220 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, #val_lit)?
221 ),
222 "Failed to defined enum `{}`",
223 #js_name_lit
224 )?;
225 };
226 })
227 }
228
229 let callback_name = Ident::new(
230 &format!("__register__enum__{}_callback__", name_str),
231 Span::call_site(),
232 );
233
234 let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
235
236 quote! {
237 #[allow(non_snake_case)]
238 #[allow(clippy::all)]
239 unsafe fn #callback_name(env: napi::bindgen_prelude::sys::napi_env) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
240 use std::ffi::CString;
241 use std::ptr;
242
243 let mut obj_ptr = ptr::null_mut();
244
245 napi::bindgen_prelude::check_status!(
246 napi::bindgen_prelude::sys::napi_create_object(env, &mut obj_ptr),
247 "Failed to create napi object"
248 )?;
249
250 #(#define_properties)*
251
252 Ok(obj_ptr)
253 }
254 #[allow(non_snake_case)]
255 #[allow(clippy::all)]
256 #[cfg(all(not(test), not(target_family = "wasm")))]
257 #[napi::ctor::ctor(crate_path=napi::ctor)]
258 fn #register_name() {
259 napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #callback_name);
260 }
261 #[allow(non_snake_case)]
262 #[allow(clippy::all)]
263 #[cfg(all(not(test), target_family = "wasm"))]
264 #[no_mangle]
265 extern "C" fn #register_name() {
266 napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #callback_name);
267 }
268 }
269 }
270}