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