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