napi_derive_backend/codegen/
struct.rs

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