napi_derive_backend/codegen/
struct.rs

1use std::collections::HashMap;
2use std::sync::atomic::{AtomicU32, Ordering};
3
4use convert_case::Casing;
5use proc_macro2::{Ident, Literal, Span, TokenStream};
6use quote::ToTokens;
7
8use crate::{
9  codegen::{get_intermediate_ident, js_mod_to_token_stream},
10  BindgenResult, FnKind, NapiImpl, NapiStruct, NapiStructKind, TryToTokens,
11};
12use crate::{NapiArray, NapiClass, NapiObject, NapiStructuredEnum, NapiTransparent};
13
14static NAPI_IMPL_ID: AtomicU32 = AtomicU32::new(0);
15
16const STRUCT_FIELD_SPECIAL_CASE: &[&str] = &["Option", "Result"];
17
18// Generate trait implementations for given Struct.
19fn gen_napi_value_map_impl(
20  name: &Ident,
21  to_napi_val_impl: TokenStream,
22  has_lifetime: bool,
23) -> TokenStream {
24  let name_str = name.to_string();
25  let name = if has_lifetime {
26    quote! { #name<'_> }
27  } else {
28    quote! { #name }
29  };
30  let js_name_str = format!("{name_str}\0");
31  let validate = quote! {
32    unsafe fn validate(env: napi::sys::napi_env, napi_val: napi::sys::napi_value) -> napi::Result<napi::sys::napi_value> {
33      if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
34        let mut ctor = std::ptr::null_mut();
35        napi::check_status!(
36          napi::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
37          "Failed to get constructor reference of class `{}`",
38          #name_str
39        )?;
40        let mut is_instance_of = false;
41        napi::check_status!(
42          napi::sys::napi_instanceof(env, napi_val, ctor, &mut is_instance_of),
43          "Failed to get external value of class `{}`",
44          #name_str
45        )?;
46        if is_instance_of {
47          Ok(std::ptr::null_mut())
48        } else {
49          Err(napi::Error::new(
50            napi::Status::InvalidArg,
51            format!("Value is not instanceof class `{}`", #name_str)
52          ))
53        }
54      } else {
55        Err(napi::Error::new(
56          napi::Status::InvalidArg,
57          format!("Failed to get constructor of class `{}`", #name_str)
58        ))
59      }
60    }
61  };
62  quote! {
63    #[automatically_derived]
64    impl napi::bindgen_prelude::TypeName for #name {
65      fn type_name() -> &'static str {
66        #name_str
67      }
68
69      fn value_type() -> napi::ValueType {
70        napi::ValueType::Function
71      }
72    }
73
74    #[automatically_derived]
75    impl napi::bindgen_prelude::TypeName for &#name {
76      fn type_name() -> &'static str {
77        #name_str
78      }
79
80      fn value_type() -> napi::ValueType {
81        napi::ValueType::Object
82      }
83    }
84
85    #[automatically_derived]
86    impl napi::bindgen_prelude::TypeName for &mut #name {
87      fn type_name() -> &'static str {
88        #name_str
89      }
90
91      fn value_type() -> napi::ValueType {
92        napi::ValueType::Object
93      }
94    }
95
96    #to_napi_val_impl
97
98    #[automatically_derived]
99    impl napi::bindgen_prelude::FromNapiRef for #name {
100      unsafe fn from_napi_ref(
101        env: napi::bindgen_prelude::sys::napi_env,
102        napi_val: napi::bindgen_prelude::sys::napi_value
103      ) -> napi::bindgen_prelude::Result<&'static Self> {
104        let mut wrapped_val: *mut std::ffi::c_void = std::ptr::null_mut();
105
106        napi::bindgen_prelude::check_status!(
107          napi::bindgen_prelude::sys::napi_unwrap(env, napi_val, &mut wrapped_val),
108          "Failed to recover `{}` type from napi value",
109          #name_str,
110        )?;
111
112        Ok(&*(wrapped_val as *const #name))
113      }
114    }
115
116    #[automatically_derived]
117    impl napi::bindgen_prelude::FromNapiMutRef for #name {
118      unsafe fn from_napi_mut_ref(
119        env: napi::bindgen_prelude::sys::napi_env,
120        napi_val: napi::bindgen_prelude::sys::napi_value
121      ) -> napi::bindgen_prelude::Result<&'static mut Self> {
122        let mut wrapped_val: *mut std::ffi::c_void = std::ptr::null_mut();
123
124        napi::bindgen_prelude::check_status!(
125          napi::bindgen_prelude::sys::napi_unwrap(env, napi_val, &mut wrapped_val),
126          "Failed to recover `{}` type from napi value",
127          #name_str,
128        )?;
129
130        Ok(&mut *(wrapped_val as *mut #name))
131      }
132    }
133
134    #[automatically_derived]
135    impl napi::bindgen_prelude::ValidateNapiValue for &#name {
136      #validate
137    }
138
139    #[automatically_derived]
140    impl napi::bindgen_prelude::ValidateNapiValue for &mut #name {
141      #validate
142    }
143  }
144}
145
146impl TryToTokens for NapiStruct {
147  fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
148    let napi_value_map_impl = self.gen_napi_value_map_impl();
149
150    let class_helper_mod = match &self.kind {
151      NapiStructKind::Class(class) => self.gen_helper_mod(class),
152      _ => quote! {},
153    };
154
155    (quote! {
156      #napi_value_map_impl
157      #class_helper_mod
158    })
159    .to_tokens(tokens);
160
161    Ok(())
162  }
163}
164
165impl NapiStruct {
166  fn gen_helper_mod(&self, class: &NapiClass) -> TokenStream {
167    let mod_name = Ident::new(&format!("__napi_helper__{}", self.name), Span::call_site());
168
169    let ctor = if class.ctor {
170      self.gen_default_ctor(class)
171    } else {
172      quote! {}
173    };
174
175    let mut getters_setters = self.gen_default_getters_setters(class);
176    getters_setters.sort_by(|a, b| a.0.cmp(&b.0));
177    let register = self.gen_register(class);
178
179    let getters_setters_token = getters_setters.into_iter().map(|(_, token)| token);
180
181    quote! {
182      #[allow(clippy::all)]
183      #[allow(non_snake_case)]
184      mod #mod_name {
185        use std::ptr;
186        use super::*;
187
188        #ctor
189        #(#getters_setters_token)*
190        #register
191      }
192    }
193  }
194
195  fn gen_default_ctor(&self, class: &NapiClass) -> TokenStream {
196    let name = &self.name;
197    let js_name_str = &self.js_name;
198    let fields_len = class.fields.len();
199    let mut fields = vec![];
200
201    for (i, field) in class.fields.iter().enumerate() {
202      let ty = &field.ty;
203      match &field.name {
204        syn::Member::Named(ident) => fields
205          .push(quote! { #ident: <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#i))? }),
206        syn::Member::Unnamed(_) => {
207          fields.push(quote! { <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#i))? });
208        }
209      }
210    }
211
212    let construct = if class.is_tuple {
213      quote! { #name (#(#fields),*) }
214    } else {
215      quote! { #name {#(#fields),*} }
216    };
217
218    let is_empty_struct_hint = fields_len == 0;
219
220    let constructor = if class.implement_iterator {
221      quote! { unsafe { cb.construct_generator::<#is_empty_struct_hint, #name>(#js_name_str, #construct) } }
222    } else {
223      quote! { unsafe { cb.construct::<#is_empty_struct_hint, #name>(#js_name_str, #construct) } }
224    };
225
226    quote! {
227      extern "C" fn constructor(
228        env: napi::bindgen_prelude::sys::napi_env,
229        cb: napi::bindgen_prelude::sys::napi_callback_info
230      ) -> napi::bindgen_prelude::sys::napi_value {
231        napi::bindgen_prelude::CallbackInfo::<#fields_len>::new(env, cb, None, false)
232          .and_then(|cb| #constructor)
233          .unwrap_or_else(|e| {
234            unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
235            std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
236          })
237      }
238    }
239  }
240
241  fn gen_napi_value_map_impl(&self) -> TokenStream {
242    match &self.kind {
243      NapiStructKind::Array(array) => self.gen_napi_value_array_impl(array),
244      NapiStructKind::Transparent(transparent) => self.gen_napi_value_transparent_impl(transparent),
245      NapiStructKind::Class(class) if !class.ctor => gen_napi_value_map_impl(
246        &self.name,
247        self.gen_to_napi_value_ctor_impl_for_non_default_constructor_struct(class),
248        self.has_lifetime,
249      ),
250      NapiStructKind::Class(class) => gen_napi_value_map_impl(
251        &self.name,
252        self.gen_to_napi_value_ctor_impl(class),
253        self.has_lifetime,
254      ),
255      NapiStructKind::Object(obj) => self.gen_to_napi_value_obj_impl(obj),
256      NapiStructKind::StructuredEnum(structured_enum) => {
257        self.gen_to_napi_value_structured_enum_impl(structured_enum)
258      }
259    }
260  }
261
262  fn gen_to_napi_value_ctor_impl_for_non_default_constructor_struct(
263    &self,
264    class: &NapiClass,
265  ) -> TokenStream {
266    let name = &self.name;
267    let js_name_raw = &self.js_name;
268    let js_name_str = format!("{js_name_raw}\0");
269    let iterator_implementation = self.gen_iterator_property(class, name);
270    let (object_finalize_impl, to_napi_value_impl, javascript_class_ext_impl) = if self.has_lifetime
271    {
272      let name = quote! { #name<'_javascript_function_scope> };
273      (
274        quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ObjectFinalize for #name {} },
275        quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name },
276        quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::JavaScriptClassExt for #name },
277      )
278    } else {
279      (
280        quote! { impl napi::bindgen_prelude::ObjectFinalize for #name {} },
281        quote! { impl napi::bindgen_prelude::ToNapiValue for #name },
282        quote! { impl napi::bindgen_prelude::JavaScriptClassExt for #name },
283      )
284    };
285    let finalize_trait = if class.use_custom_finalize {
286      quote! {}
287    } else {
288      quote! {
289        #[automatically_derived]
290        #object_finalize_impl
291      }
292    };
293    quote! {
294      #[automatically_derived]
295      #to_napi_value_impl {
296        unsafe fn to_napi_value(
297          env: napi::sys::napi_env,
298          val: #name
299        ) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
300          if let Some(ctor_ref) = napi::__private::get_class_constructor(#js_name_str) {
301            let mut wrapped_value = Box::into_raw(Box::new(val));
302            if wrapped_value as usize == 0x1 {
303              wrapped_value = Box::into_raw(Box::new(0u8)).cast();
304            }
305            let instance_value = napi::bindgen_prelude::new_instance::<#name>(env, wrapped_value.cast(), ctor_ref)?;
306            #iterator_implementation
307            Ok(instance_value)
308          } else {
309            Err(napi::bindgen_prelude::Error::new(
310              napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}` in `ToNapiValue`", #js_name_raw))
311            )
312          }
313        }
314      }
315
316      #finalize_trait
317
318      #[automatically_derived]
319      #javascript_class_ext_impl {
320        fn into_instance<'scope>(self, env: &'scope napi::Env) -> napi::Result<napi::bindgen_prelude::ClassInstance<'scope, Self>>
321         {
322          if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
323            unsafe {
324              let wrapped_value = Box::into_raw(Box::new(self));
325              let instance_value = napi::bindgen_prelude::new_instance::<#name>(env.raw(), wrapped_value as *mut _ as *mut std::ffi::c_void, ctor_ref)?;
326              Ok(napi::bindgen_prelude::ClassInstance::new(instance_value, env.raw(), wrapped_value))
327            }
328          } else {
329            Err(napi::bindgen_prelude::Error::new(
330              napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
331            )
332          }
333        }
334
335        fn into_reference(self, env: napi::Env) -> napi::Result<napi::bindgen_prelude::Reference<Self>> {
336          if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
337            unsafe {
338              let mut wrapped_value = Box::into_raw(Box::new(self));
339              if wrapped_value as usize == 0x1 {
340                wrapped_value = Box::into_raw(Box::new(0u8)).cast();
341              }
342              let instance_value = napi::bindgen_prelude::new_instance::<#name>(env.raw(), wrapped_value.cast(), ctor_ref)?;
343              {
344                let env = env.raw();
345                #iterator_implementation
346              }
347              napi::bindgen_prelude::Reference::<#name>::from_value_ptr(wrapped_value.cast(), env.raw())
348            }
349          } else {
350            Err(napi::bindgen_prelude::Error::new(
351              napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
352            )
353          }
354        }
355
356        fn instance_of<'env, V: napi::JsValue<'env>>(env: &napi::bindgen_prelude::Env, value: &V) -> napi::bindgen_prelude::Result<bool> {
357          if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
358            let mut ctor = std::ptr::null_mut();
359            napi::check_status!(
360              unsafe { napi::sys::napi_get_reference_value(env.raw(), ctor_ref, &mut ctor) },
361              "Failed to get constructor reference of class `{}`",
362              #js_name_str
363            )?;
364            let mut is_instance_of = false;
365            napi::check_status!(
366              unsafe { napi::sys::napi_instanceof(env.raw(), value.value().value, ctor, &mut is_instance_of) },
367              "Failed to run instanceof for class `{}`",
368              #js_name_str
369            )?;
370            Ok(is_instance_of)
371          } else {
372            Err(napi::Error::new(napi::Status::GenericFailure, format!("Failed to get constructor of class `{}`", #js_name_str)))
373          }
374        }
375      }
376    }
377  }
378
379  fn gen_iterator_property(&self, class: &NapiClass, name: &Ident) -> TokenStream {
380    if !class.implement_iterator {
381      return quote! {};
382    }
383    quote! {
384      unsafe { napi::__private::create_iterator::<#name>(env, instance_value, wrapped_value); }
385    }
386  }
387
388  fn gen_to_napi_value_ctor_impl(&self, class: &NapiClass) -> TokenStream {
389    let name = &self.name;
390    let js_name_without_null = &self.js_name;
391    let js_name_str = format!("{}\0", &self.js_name);
392
393    let mut field_conversions = vec![];
394    let mut field_destructions = vec![];
395
396    for field in class.fields.iter() {
397      let ty = &field.ty;
398
399      match &field.name {
400        syn::Member::Named(ident) => {
401          // alias here prevents field name shadowing
402          let alias_ident = format_ident!("{}_", ident);
403          field_destructions.push(quote! { #ident: #alias_ident });
404          field_conversions.push(
405            quote! { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #alias_ident)? },
406          );
407        }
408        syn::Member::Unnamed(i) => {
409          let arg_name = format_ident!("arg{}", i);
410          field_destructions.push(quote! { #arg_name });
411          field_conversions.push(
412            quote! { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #arg_name)? },
413          );
414        }
415      }
416    }
417
418    let destructed_fields = if class.is_tuple {
419      quote! {
420        Self (#(#field_destructions),*)
421      }
422    } else {
423      quote! {
424        Self {#(#field_destructions),*}
425      }
426    };
427
428    let finalize_trait = if class.use_custom_finalize {
429      quote! {}
430    } else if self.has_lifetime {
431      quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ObjectFinalize for #name<'_javascript_function_scope> {} }
432    } else {
433      quote! { impl napi::bindgen_prelude::ObjectFinalize for #name {} }
434    };
435
436    let to_napi_value_impl = if self.has_lifetime {
437      quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name<'_javascript_function_scope> }
438    } else {
439      quote! { impl napi::bindgen_prelude::ToNapiValue for #name }
440    };
441
442    quote! {
443      #[automatically_derived]
444      #to_napi_value_impl {
445        unsafe fn to_napi_value(
446          env: napi::bindgen_prelude::sys::napi_env,
447          val: #name,
448        ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
449          if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
450            let mut ctor = std::ptr::null_mut();
451
452            napi::bindgen_prelude::check_status!(
453              napi::bindgen_prelude::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
454              "Failed to get constructor reference of class `{}`",
455              #js_name_without_null
456            )?;
457
458            let mut instance_value = std::ptr::null_mut();
459            let #destructed_fields = val;
460            let args = vec![#(#field_conversions),*];
461
462            napi::bindgen_prelude::check_status!(
463              napi::bindgen_prelude::sys::napi_new_instance(env, ctor, args.len(), args.as_ptr(), &mut instance_value),
464              "Failed to construct class `{}`",
465              #js_name_without_null
466            )?;
467
468            Ok(instance_value)
469          } else {
470            Err(napi::bindgen_prelude::Error::new(
471              napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_str))
472            )
473          }
474        }
475      }
476      #finalize_trait
477    }
478  }
479
480  fn gen_to_napi_value_obj_impl(&self, obj: &NapiObject) -> TokenStream {
481    let name = &self.name;
482    let name_str = self.name.to_string();
483
484    let mut obj_field_setters = vec![];
485    let mut obj_field_getters = vec![];
486    let mut field_destructions = vec![];
487
488    for field in obj.fields.iter() {
489      let field_js_name = &field.js_name;
490      let mut ty = field.ty.clone();
491      remove_lifetime_in_type(&mut ty);
492      let is_optional_field = if let syn::Type::Path(syn::TypePath {
493        path: syn::Path { segments, .. },
494        ..
495      }) = &ty
496      {
497        if let Some(last_path) = segments.last() {
498          last_path.ident == "Option"
499        } else {
500          false
501        }
502      } else {
503        false
504      };
505      match &field.name {
506        syn::Member::Named(ident) => {
507          let alias_ident = format_ident!("{}_", ident);
508          field_destructions.push(quote! { #ident: #alias_ident });
509          if is_optional_field {
510            obj_field_setters.push(match self.use_nullable {
511              false => quote! {
512                if #alias_ident.is_some() {
513                  obj.set(#field_js_name, #alias_ident)?;
514                }
515              },
516              true => quote! {
517                if let Some(#alias_ident) = #alias_ident {
518                  obj.set(#field_js_name, #alias_ident)?;
519                } else {
520                  obj.set(#field_js_name, napi::bindgen_prelude::Null)?;
521                }
522              },
523            });
524          } else {
525            obj_field_setters.push(quote! { obj.set(#field_js_name, #alias_ident)?; });
526          }
527          if is_optional_field && !self.use_nullable {
528            obj_field_getters.push(quote! {
529              let #alias_ident: #ty = obj.get(#field_js_name).map_err(|mut err| {
530                err.reason = format!("{} on {}.{}", err.reason, #name_str, #field_js_name);
531                err
532              })?;
533            });
534          } else {
535            obj_field_getters.push(quote! {
536              let #alias_ident: #ty = obj.get(#field_js_name).map_err(|mut err| {
537                err.reason = format!("{} on {}.{}", err.reason, #name_str, #field_js_name);
538                err
539              })?.ok_or_else(|| napi::bindgen_prelude::Error::new(
540                napi::bindgen_prelude::Status::InvalidArg,
541                format!("Missing field `{}`", #field_js_name),
542              ))?;
543            });
544          }
545        }
546        syn::Member::Unnamed(i) => {
547          let arg_name = format_ident!("arg{}", i);
548          field_destructions.push(quote! { #arg_name });
549          if is_optional_field {
550            obj_field_setters.push(match self.use_nullable {
551              false => quote! {
552                if #arg_name.is_some() {
553                  obj.set(#field_js_name, #arg_name)?;
554                }
555              },
556              true => quote! {
557                if let Some(#arg_name) = #arg_name {
558                  obj.set(#field_js_name, #arg_name)?;
559                } else {
560                  obj.set(#field_js_name, napi::bindgen_prelude::Null)?;
561                }
562              },
563            });
564          } else {
565            obj_field_setters.push(quote! { obj.set(#field_js_name, #arg_name)?; });
566          }
567          if is_optional_field && !self.use_nullable {
568            obj_field_getters.push(quote! { let #arg_name: #ty = obj.get(#field_js_name)?; });
569          } else {
570            obj_field_getters.push(quote! {
571              let #arg_name: #ty = obj.get(#field_js_name)?.ok_or_else(|| napi::bindgen_prelude::Error::new(
572                napi::bindgen_prelude::Status::InvalidArg,
573                format!("Missing field `{}`", #field_js_name),
574              ))?;
575            });
576          }
577        }
578      }
579    }
580
581    let destructed_fields = if obj.is_tuple {
582      quote! {
583        Self (#(#field_destructions),*)
584      }
585    } else {
586      quote! {
587        Self {#(#field_destructions),*}
588      }
589    };
590
591    let name_with_lifetime = if self.has_lifetime {
592      quote! { #name<'_javascript_function_scope> }
593    } else {
594      quote! { #name }
595    };
596    let (from_napi_value_impl, to_napi_value_impl, validate_napi_value_impl, type_name_impl) =
597      if self.has_lifetime {
598        (
599          quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::FromNapiValue for #name<'_javascript_function_scope> },
600          quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name<'_javascript_function_scope> },
601          quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ValidateNapiValue for #name<'_javascript_function_scope> },
602          quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::TypeName for #name<'_javascript_function_scope> },
603        )
604      } else {
605        (
606          quote! { impl napi::bindgen_prelude::FromNapiValue for #name },
607          quote! { impl napi::bindgen_prelude::ToNapiValue for #name },
608          quote! { impl napi::bindgen_prelude::ValidateNapiValue for #name },
609          quote! { impl napi::bindgen_prelude::TypeName for #name },
610        )
611      };
612
613    let to_napi_value = if obj.object_to_js {
614      quote! {
615        #[automatically_derived]
616        #to_napi_value_impl {
617          unsafe fn to_napi_value(env: napi::bindgen_prelude::sys::napi_env, val: #name_with_lifetime) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
618            #[allow(unused_variables)]
619            let env_wrapper = napi::bindgen_prelude::Env::from(env);
620            #[allow(unused_mut)]
621            let mut obj = napi::bindgen_prelude::Object::new(&env_wrapper)?;
622
623            let #destructed_fields = val;
624            #(#obj_field_setters)*
625
626            napi::bindgen_prelude::Object::to_napi_value(env, obj)
627          }
628        }
629      }
630    } else {
631      quote! {}
632    };
633
634    let from_napi_value = if obj.object_from_js {
635      let return_type = if self.has_lifetime {
636        quote! { #name<'_javascript_function_scope> }
637      } else {
638        quote! { #name }
639      };
640      quote! {
641        #[automatically_derived]
642        #from_napi_value_impl {
643          unsafe fn from_napi_value(
644            env: napi::bindgen_prelude::sys::napi_env,
645            napi_val: napi::bindgen_prelude::sys::napi_value
646          ) -> napi::bindgen_prelude::Result<#return_type> {
647            #[allow(unused_variables)]
648            let env_wrapper = napi::bindgen_prelude::Env::from(env);
649            #[allow(unused_mut)]
650            let mut obj = napi::bindgen_prelude::Object::from_napi_value(env, napi_val)?;
651
652            #(#obj_field_getters)*
653
654            let val = #destructed_fields;
655
656            Ok(val)
657          }
658        }
659
660        #[automatically_derived]
661        #validate_napi_value_impl {}
662      }
663    } else {
664      quote! {}
665    };
666
667    quote! {
668      #[automatically_derived]
669      #type_name_impl {
670        fn type_name() -> &'static str {
671          #name_str
672        }
673
674        fn value_type() -> napi::ValueType {
675          napi::ValueType::Object
676        }
677      }
678
679      #to_napi_value
680
681      #from_napi_value
682    }
683  }
684
685  fn gen_default_getters_setters(&self, class: &NapiClass) -> Vec<(String, TokenStream)> {
686    let mut getters_setters = vec![];
687    let struct_name = &self.name;
688
689    for field in class.fields.iter() {
690      let field_ident = &field.name;
691      let field_name = match &field.name {
692        syn::Member::Named(ident) => ident.to_string(),
693        syn::Member::Unnamed(i) => format!("field{}", i.index),
694      };
695      let ty = &field.ty;
696
697      let getter_name = Ident::new(
698        &format!("get_{}", rm_raw_prefix(&field_name)),
699        Span::call_site(),
700      );
701      let setter_name = Ident::new(
702        &format!("set_{}", rm_raw_prefix(&field_name)),
703        Span::call_site(),
704      );
705
706      if field.getter {
707        let default_to_napi_value_convert = quote! {
708          let val = &mut obj.#field_ident;
709          unsafe { <&mut #ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) }
710        };
711        let to_napi_value_convert = if let syn::Type::Path(syn::TypePath {
712          path: syn::Path { segments, .. },
713          ..
714        }) = ty
715        {
716          if let Some(syn::PathSegment { ident, .. }) = segments.last() {
717            if STRUCT_FIELD_SPECIAL_CASE.iter().any(|name| ident == name) {
718              quote! {
719                let val = obj.#field_ident.as_mut();
720                unsafe { napi::bindgen_prelude::ToNapiValue::to_napi_value(env, val) }
721              }
722            } else {
723              default_to_napi_value_convert
724            }
725          } else {
726            default_to_napi_value_convert
727          }
728        } else {
729          default_to_napi_value_convert
730        };
731        getters_setters.push((
732          field.js_name.clone(),
733          quote! {
734            extern "C" fn #getter_name(
735              env: napi::bindgen_prelude::sys::napi_env,
736              cb: napi::bindgen_prelude::sys::napi_callback_info
737            ) -> napi::bindgen_prelude::sys::napi_value {
738              napi::bindgen_prelude::CallbackInfo::<0>::new(env, cb, Some(0), false)
739                .and_then(|mut cb| cb.unwrap_borrow_mut::<#struct_name>())
740                .and_then(|obj| {
741                  #to_napi_value_convert
742                })
743                .unwrap_or_else(|e| {
744                  unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
745                  std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
746                })
747            }
748          },
749        ));
750      }
751
752      if field.setter {
753        getters_setters.push((
754          field.js_name.clone(),
755          quote! {
756            extern "C" fn #setter_name(
757              env: napi::bindgen_prelude::sys::napi_env,
758              cb: napi::bindgen_prelude::sys::napi_callback_info
759            ) -> napi::bindgen_prelude::sys::napi_value {
760              napi::bindgen_prelude::CallbackInfo::<1>::new(env, cb, Some(1), false)
761                .and_then(|mut cb_info| unsafe {
762                  cb_info.unwrap_borrow_mut::<#struct_name>()
763                    .and_then(|obj| {
764                      <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb_info.get_arg(0))
765                        .and_then(move |val| {
766                          obj.#field_ident = val;
767                          <() as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, ())
768                        })
769                    })
770                })
771                .unwrap_or_else(|e| {
772                  unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
773                  std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
774                })
775            }
776          },
777        ));
778      }
779    }
780
781    getters_setters
782  }
783
784  fn gen_register(&self, class: &NapiClass) -> TokenStream {
785    let name = &self.name;
786    let struct_register_name = &self.register_name;
787    let js_name = format!("{}\0", self.js_name);
788    let mut props = vec![];
789
790    if class.ctor {
791      props.push(quote! { napi::bindgen_prelude::Property::new().with_utf8_name("constructor").unwrap().with_ctor(constructor) });
792    }
793
794    for field in class.fields.iter() {
795      let field_name = match &field.name {
796        syn::Member::Named(ident) => ident.to_string(),
797        syn::Member::Unnamed(i) => format!("field{}", i.index),
798      };
799
800      if !field.getter {
801        continue;
802      }
803
804      let js_name = &field.js_name;
805      let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT;
806      if field.writable {
807        attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE;
808      }
809      if field.enumerable {
810        attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE;
811      }
812      if field.configurable {
813        attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE;
814      }
815
816      let mut prop = quote! {
817        napi::bindgen_prelude::Property::new().with_utf8_name(#js_name)
818          .unwrap()
819          .with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap())
820      };
821
822      if field.getter {
823        let getter_name = Ident::new(
824          &format!("get_{}", rm_raw_prefix(&field_name)),
825          Span::call_site(),
826        );
827        (quote! { .with_getter(#getter_name) }).to_tokens(&mut prop);
828      }
829
830      if field.writable && field.setter {
831        let setter_name = Ident::new(
832          &format!("set_{}", rm_raw_prefix(&field_name)),
833          Span::call_site(),
834        );
835        (quote! { .with_setter(#setter_name) }).to_tokens(&mut prop);
836      }
837
838      props.push(prop);
839    }
840    let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
841    quote! {
842      #[allow(non_snake_case)]
843      #[allow(clippy::all)]
844      #[cfg(all(not(test), not(target_family = "wasm")))]
845      #[napi::ctor::ctor(crate_path=napi::ctor)]
846      fn #struct_register_name() {
847        napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*]);
848      }
849
850      #[allow(non_snake_case)]
851      #[allow(clippy::all)]
852      #[cfg(all(not(test), target_family = "wasm"))]
853      #[no_mangle]
854      extern "C" fn #struct_register_name() {
855        napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*]);
856      }
857    }
858  }
859
860  fn gen_to_napi_value_structured_enum_impl(
861    &self,
862    structured_enum: &NapiStructuredEnum,
863  ) -> TokenStream {
864    let name = &self.name;
865    let name_str = self.name.to_string();
866    let discriminant = structured_enum.discriminant.as_str();
867
868    let mut variant_arm_setters = vec![];
869    let mut variant_arm_getters = vec![];
870
871    for variant in structured_enum.variants.iter() {
872      let variant_name = &variant.name;
873      let mut variant_name_str = variant_name.to_string();
874      if let Some(case) = structured_enum.discriminant_case {
875        variant_name_str = variant_name_str.to_case(case);
876      }
877      let mut obj_field_setters = vec![quote! {
878        obj.set(#discriminant, #variant_name_str)?;
879      }];
880      let mut obj_field_getters = vec![];
881      let mut field_destructions = vec![];
882      for field in variant.fields.iter() {
883        let field_js_name = &field.js_name;
884        let mut ty = field.ty.clone();
885        remove_lifetime_in_type(&mut ty);
886        let is_optional_field = if let syn::Type::Path(syn::TypePath {
887          path: syn::Path { segments, .. },
888          ..
889        }) = &ty
890        {
891          if let Some(last_path) = segments.last() {
892            last_path.ident == "Option"
893          } else {
894            false
895          }
896        } else {
897          false
898        };
899        match &field.name {
900          syn::Member::Named(ident) => {
901            let alias_ident = format_ident!("{}_", ident);
902            field_destructions.push(quote! { #ident: #alias_ident });
903            if is_optional_field {
904              obj_field_setters.push(match self.use_nullable {
905                false => quote! {
906                  if #alias_ident.is_some() {
907                    obj.set(#field_js_name, #alias_ident)?;
908                  }
909                },
910                true => quote! {
911                  if let Some(#alias_ident) = #alias_ident {
912                    obj.set(#field_js_name, #alias_ident)?;
913                  } else {
914                    obj.set(#field_js_name, napi::bindgen_prelude::Null)?;
915                  }
916                },
917              });
918            } else {
919              obj_field_setters.push(quote! { obj.set(#field_js_name, #alias_ident)?; });
920            }
921            if is_optional_field && !self.use_nullable {
922              obj_field_getters.push(quote! {
923                let #alias_ident: #ty = obj.get(#field_js_name).map_err(|mut err| {
924                  err.reason = format!("{} on {}.{}", err.reason, #name_str, #field_js_name);
925                  err
926                })?;
927              });
928            } else {
929              obj_field_getters.push(quote! {
930                let #alias_ident: #ty = obj.get(#field_js_name).map_err(|mut err| {
931                  err.reason = format!("{} on {}.{}", err.reason, #name_str, #field_js_name);
932                  err
933                })?.ok_or_else(|| napi::bindgen_prelude::Error::new(
934                  napi::bindgen_prelude::Status::InvalidArg,
935                  format!("Missing field `{}`", #field_js_name),
936                ))?;
937              });
938            }
939          }
940          syn::Member::Unnamed(i) => {
941            let arg_name = format_ident!("arg{}", i);
942            field_destructions.push(quote! { #arg_name });
943            if is_optional_field {
944              obj_field_setters.push(match self.use_nullable {
945                false => quote! {
946                  if #arg_name.is_some() {
947                    obj.set(#field_js_name, #arg_name)?;
948                  }
949                },
950                true => quote! {
951                  if let Some(#arg_name) = #arg_name {
952                    obj.set(#field_js_name, #arg_name)?;
953                  } else {
954                    obj.set(#field_js_name, napi::bindgen_prelude::Null)?;
955                  }
956                },
957              });
958            } else {
959              obj_field_setters.push(quote! { obj.set(#field_js_name, #arg_name)?; });
960            }
961            if is_optional_field && !self.use_nullable {
962              obj_field_getters.push(quote! { let #arg_name: #ty = obj.get(#field_js_name)?; });
963            } else {
964              obj_field_getters.push(quote! {
965              let #arg_name: #ty = obj.get(#field_js_name)?.ok_or_else(|| napi::bindgen_prelude::Error::new(
966                napi::bindgen_prelude::Status::InvalidArg,
967                format!("Missing field `{}`", #field_js_name),
968              ))?;
969            });
970            }
971          }
972        }
973      }
974
975      let destructed_fields = if variant.is_tuple {
976        quote! {
977          Self::#variant_name (#(#field_destructions),*)
978        }
979      } else {
980        quote! {
981          Self::#variant_name {#(#field_destructions),*}
982        }
983      };
984
985      variant_arm_setters.push(quote! {
986        #destructed_fields => {
987          #(#obj_field_setters)*
988        },
989      });
990
991      variant_arm_getters.push(quote! {
992        #variant_name_str => {
993          #(#obj_field_getters)*
994          #destructed_fields
995        },
996      })
997    }
998
999    let to_napi_value = if structured_enum.object_to_js {
1000      quote! {
1001        impl napi::bindgen_prelude::ToNapiValue for #name {
1002          unsafe fn to_napi_value(env: napi::bindgen_prelude::sys::napi_env, val: #name) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
1003            #[allow(unused_variables)]
1004            let env_wrapper = napi::bindgen_prelude::Env::from(env);
1005            #[allow(unused_mut)]
1006            let mut obj = napi::bindgen_prelude::Object::new(&env_wrapper)?;
1007            match val {
1008              #(#variant_arm_setters)*
1009            };
1010
1011            napi::bindgen_prelude::Object::to_napi_value(env, obj)
1012          }
1013        }
1014      }
1015    } else {
1016      quote! {}
1017    };
1018
1019    let from_napi_value = if structured_enum.object_from_js {
1020      quote! {
1021        impl napi::bindgen_prelude::FromNapiValue for #name {
1022          unsafe fn from_napi_value(
1023            env: napi::bindgen_prelude::sys::napi_env,
1024            napi_val: napi::bindgen_prelude::sys::napi_value
1025          ) -> napi::bindgen_prelude::Result<Self> {
1026            #[allow(unused_variables)]
1027            let env_wrapper = napi::bindgen_prelude::Env::from(env);
1028            #[allow(unused_mut)]
1029            let mut obj = napi::bindgen_prelude::Object::from_napi_value(env, napi_val)?;
1030            let type_: String = obj.get(#discriminant).map_err(|mut err| {
1031              err.reason = format!("{} on {}.{}", err.reason, #name_str, #discriminant);
1032              err
1033            })?.ok_or_else(|| napi::bindgen_prelude::Error::new(
1034              napi::bindgen_prelude::Status::InvalidArg,
1035              format!("Missing field `{}`", #discriminant),
1036            ))?;
1037            let val = match type_.as_str() {
1038              #(#variant_arm_getters)*
1039              _ => return Err(napi::bindgen_prelude::Error::new(
1040                napi::bindgen_prelude::Status::InvalidArg,
1041                format!("Unknown variant `{}`", type_),
1042              )),
1043            };
1044
1045            Ok(val)
1046          }
1047        }
1048
1049        impl napi::bindgen_prelude::ValidateNapiValue for #name {}
1050      }
1051    } else {
1052      quote! {}
1053    };
1054
1055    quote! {
1056      impl napi::bindgen_prelude::TypeName for #name {
1057        fn type_name() -> &'static str {
1058          #name_str
1059        }
1060
1061        fn value_type() -> napi::ValueType {
1062          napi::ValueType::Object
1063        }
1064      }
1065
1066      #to_napi_value
1067
1068      #from_napi_value
1069    }
1070  }
1071
1072  fn gen_napi_value_transparent_impl(&self, transparent: &NapiTransparent) -> TokenStream {
1073    let name = &self.name;
1074    let name = if self.has_lifetime {
1075      quote! { #name<'_> }
1076    } else {
1077      quote! { #name }
1078    };
1079    let inner_type = transparent.ty.clone().into_token_stream();
1080
1081    let to_napi_value = if transparent.object_to_js {
1082      quote! {
1083        #[automatically_derived]
1084        impl napi::bindgen_prelude::ToNapiValue for #name {
1085          unsafe fn to_napi_value(
1086            env: napi::bindgen_prelude::sys::napi_env,
1087            val: Self
1088          ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
1089            <#inner_type>::to_napi_value(env, val.0)
1090          }
1091        }
1092      }
1093    } else {
1094      quote! {}
1095    };
1096
1097    let from_napi_value = if transparent.object_from_js {
1098      quote! {
1099        #[automatically_derived]
1100        impl napi::bindgen_prelude::FromNapiValue for #name {
1101          unsafe fn from_napi_value(
1102            env: napi::bindgen_prelude::sys::napi_env,
1103            napi_val: napi::bindgen_prelude::sys::napi_value
1104          ) -> napi::bindgen_prelude::Result<Self> {
1105            Ok(Self(<#inner_type>::from_napi_value(env, napi_val)?))
1106          }
1107        }
1108      }
1109    } else {
1110      quote! {}
1111    };
1112
1113    quote! {
1114      #[automatically_derived]
1115      impl napi::bindgen_prelude::TypeName for #name {
1116        fn type_name() -> &'static str {
1117          <#inner_type>::type_name()
1118        }
1119
1120        fn value_type() -> napi::ValueType {
1121          <#inner_type>::value_type()
1122        }
1123      }
1124
1125      #[automatically_derived]
1126      impl napi::bindgen_prelude::ValidateNapiValue for #name {
1127        unsafe fn validate(
1128          env: napi::bindgen_prelude::sys::napi_env,
1129          napi_val: napi::bindgen_prelude::sys::napi_value
1130        ) -> napi::bindgen_prelude::Result<napi::sys::napi_value> {
1131          <#inner_type>::validate(env, napi_val)
1132        }
1133      }
1134
1135      #to_napi_value
1136
1137      #from_napi_value
1138    }
1139  }
1140
1141  fn gen_napi_value_array_impl(&self, array: &NapiArray) -> TokenStream {
1142    let name = &self.name;
1143    let name_str = self.name.to_string();
1144
1145    let mut obj_field_setters = vec![];
1146    let mut obj_field_getters = vec![];
1147    let mut field_destructions = vec![];
1148
1149    for field in array.fields.iter() {
1150      let mut ty = field.ty.clone();
1151      remove_lifetime_in_type(&mut ty);
1152      let is_optional_field = if let syn::Type::Path(syn::TypePath {
1153        path: syn::Path { segments, .. },
1154        ..
1155      }) = &ty
1156      {
1157        if let Some(last_path) = segments.last() {
1158          last_path.ident == "Option"
1159        } else {
1160          false
1161        }
1162      } else {
1163        false
1164      };
1165
1166      if let syn::Member::Unnamed(i) = &field.name {
1167        let arg_name = format_ident!("arg{}", i);
1168        let field_index = i.index;
1169        field_destructions.push(quote! { #arg_name });
1170        if is_optional_field {
1171          obj_field_setters.push(match self.use_nullable {
1172            false => quote! {
1173              if #arg_name.is_some() {
1174                array.set(#field_index, #arg_name)?;
1175              }
1176            },
1177            true => quote! {
1178              if let Some(#arg_name) = #arg_name {
1179                array.set(#field_index, #arg_name)?;
1180              } else {
1181                array.set(#field_index, napi::bindgen_prelude::Null)?;
1182              }
1183            },
1184          });
1185        } else {
1186          obj_field_setters.push(quote! { array.set(#field_index, #arg_name)?; });
1187        }
1188        if is_optional_field && !self.use_nullable {
1189          obj_field_getters.push(quote! { let #arg_name: #ty = array.get(#field_index)?; });
1190        } else {
1191          obj_field_getters.push(quote! {
1192            let #arg_name: #ty = array.get(#field_index)?.ok_or_else(|| napi::bindgen_prelude::Error::new(
1193              napi::bindgen_prelude::Status::InvalidArg,
1194              format!("Failed to get element with index `{}`", #field_index),
1195            ))?;
1196          });
1197        }
1198      }
1199    }
1200
1201    let destructed_fields = quote! {
1202      Self (#(#field_destructions),*)
1203    };
1204
1205    let name_with_lifetime = if self.has_lifetime {
1206      quote! { #name<'_javascript_function_scope> }
1207    } else {
1208      quote! { #name }
1209    };
1210    let (from_napi_value_impl, to_napi_value_impl, validate_napi_value_impl, type_name_impl) =
1211      if self.has_lifetime {
1212        (
1213          quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::FromNapiValue for #name<'_javascript_function_scope> },
1214          quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name<'_javascript_function_scope> },
1215          quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ValidateNapiValue for #name<'_javascript_function_scope> },
1216          quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::TypeName for #name<'_javascript_function_scope> },
1217        )
1218      } else {
1219        (
1220          quote! { impl napi::bindgen_prelude::FromNapiValue for #name },
1221          quote! { impl napi::bindgen_prelude::ToNapiValue for #name },
1222          quote! { impl napi::bindgen_prelude::ValidateNapiValue for #name },
1223          quote! { impl napi::bindgen_prelude::TypeName for #name },
1224        )
1225      };
1226
1227    let array_len = array.fields.len() as u32;
1228
1229    let to_napi_value = if array.object_to_js {
1230      quote! {
1231        #[automatically_derived]
1232        #to_napi_value_impl {
1233          unsafe fn to_napi_value(env: napi::bindgen_prelude::sys::napi_env, val: #name_with_lifetime) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
1234            #[allow(unused_variables)]
1235            let env_wrapper = napi::bindgen_prelude::Env::from(env);
1236            #[allow(unused_mut)]
1237            let mut array = env_wrapper.create_array(#array_len)?;
1238
1239            let #destructed_fields = val;
1240            #(#obj_field_setters)*
1241
1242            napi::bindgen_prelude::Array::to_napi_value(env, array)
1243          }
1244        }
1245      }
1246    } else {
1247      quote! {}
1248    };
1249
1250    let from_napi_value = if array.object_from_js {
1251      let return_type = if self.has_lifetime {
1252        quote! { #name<'_javascript_function_scope> }
1253      } else {
1254        quote! { #name }
1255      };
1256      quote! {
1257        #[automatically_derived]
1258        #from_napi_value_impl {
1259          unsafe fn from_napi_value(
1260            env: napi::bindgen_prelude::sys::napi_env,
1261            napi_val: napi::bindgen_prelude::sys::napi_value
1262          ) -> napi::bindgen_prelude::Result<#return_type> {
1263            #[allow(unused_variables)]
1264            let env_wrapper = napi::bindgen_prelude::Env::from(env);
1265            #[allow(unused_mut)]
1266            let mut array = napi::bindgen_prelude::Array::from_napi_value(env, napi_val)?;
1267
1268            #(#obj_field_getters)*
1269
1270            let val = #destructed_fields;
1271
1272            Ok(val)
1273          }
1274        }
1275
1276        #[automatically_derived]
1277        #validate_napi_value_impl {}
1278      }
1279    } else {
1280      quote! {}
1281    };
1282
1283    quote! {
1284      #[automatically_derived]
1285      #type_name_impl {
1286        fn type_name() -> &'static str {
1287          #name_str
1288        }
1289
1290        fn value_type() -> napi::ValueType {
1291          napi::ValueType::Object
1292        }
1293      }
1294
1295      #to_napi_value
1296
1297      #from_napi_value
1298    }
1299  }
1300}
1301
1302impl TryToTokens for NapiImpl {
1303  fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
1304    self.gen_helper_mod()?.to_tokens(tokens);
1305
1306    Ok(())
1307  }
1308}
1309
1310impl NapiImpl {
1311  fn gen_helper_mod(&self) -> BindgenResult<TokenStream> {
1312    if cfg!(test) {
1313      return Ok(quote! {});
1314    }
1315
1316    let name = &self.name;
1317    let name_str = self.name.to_string();
1318    let js_name = format!("{}\0", self.js_name);
1319    let mod_name = Ident::new(
1320      &format!(
1321        "__napi_impl_helper_{}_{}",
1322        name_str,
1323        NAPI_IMPL_ID.fetch_add(1, Ordering::SeqCst)
1324      ),
1325      Span::call_site(),
1326    );
1327
1328    let register_name = &self.register_name;
1329
1330    let mut methods = vec![];
1331    let mut props = HashMap::new();
1332
1333    for item in self.items.iter() {
1334      let js_name = Literal::string(&item.js_name);
1335      let item_str = item.name.to_string();
1336      let intermediate_name = get_intermediate_ident(&item_str);
1337      methods.push(item.try_to_token_stream()?);
1338
1339      let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT;
1340      if item.writable {
1341        attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE;
1342      }
1343      if item.enumerable {
1344        attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE;
1345      }
1346      if item.configurable {
1347        attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE;
1348      }
1349
1350      let prop = props.entry(&item.js_name).or_insert_with(|| {
1351        quote! {
1352          napi::bindgen_prelude::Property::new().with_utf8_name(#js_name).unwrap().with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap())
1353        }
1354      });
1355
1356      let appendix = match item.kind {
1357        FnKind::Constructor => quote! { .with_ctor(#intermediate_name) },
1358        FnKind::Getter => quote! { .with_getter(#intermediate_name) },
1359        FnKind::Setter => quote! { .with_setter(#intermediate_name) },
1360        _ => {
1361          if item.fn_self.is_some() {
1362            quote! { .with_method(#intermediate_name) }
1363          } else {
1364            quote! { .with_method(#intermediate_name).with_property_attributes(napi::bindgen_prelude::PropertyAttributes::Static) }
1365          }
1366        }
1367      };
1368
1369      appendix.to_tokens(prop);
1370    }
1371
1372    let mut props: Vec<_> = props.into_iter().collect();
1373    props.sort_by_key(|(_, prop)| prop.to_string());
1374    let props = props.into_iter().map(|(_, prop)| prop);
1375    let props_wasm = props.clone();
1376    let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
1377    Ok(quote! {
1378      #[allow(non_snake_case)]
1379      #[allow(clippy::all)]
1380      mod #mod_name {
1381        use super::*;
1382        #(#methods)*
1383
1384        #[cfg(all(not(test), not(target_family = "wasm")))]
1385        #[napi::ctor::ctor(crate_path=napi::ctor)]
1386        fn #register_name() {
1387          napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*]);
1388        }
1389
1390        #[cfg(all(not(test), target_family = "wasm"))]
1391        #[no_mangle]
1392        extern "C" fn #register_name() {
1393          napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props_wasm),*]);
1394        }
1395      }
1396    })
1397  }
1398}
1399
1400pub fn rm_raw_prefix(s: &str) -> &str {
1401  if let Some(stripped) = s.strip_prefix("r#") {
1402    stripped
1403  } else {
1404    s
1405  }
1406}
1407
1408fn remove_lifetime_in_type(ty: &mut syn::Type) {
1409  if let syn::Type::Path(syn::TypePath { path, .. }) = ty {
1410    path.segments.iter_mut().for_each(|segment| {
1411      if let syn::PathArguments::AngleBracketed(ref mut args) = segment.arguments {
1412        args.args.iter_mut().for_each(|arg| match arg {
1413          syn::GenericArgument::Type(ref mut ty) => {
1414            remove_lifetime_in_type(ty);
1415          }
1416          syn::GenericArgument::Lifetime(lifetime) => {
1417            lifetime.ident = Ident::new("_", lifetime.ident.span());
1418          }
1419          _ => {}
1420        });
1421      }
1422    });
1423  }
1424}