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