1use std::collections::HashMap;
2use std::sync::atomic::{AtomicU32, Ordering};
3
4use proc_macro2::{Ident, Literal, Span, TokenStream};
5use quote::ToTokens;
6
7use crate::util::to_case;
8
9use crate::{
10 codegen::{get_intermediate_ident, js_mod_to_token_stream},
11 BindgenResult, FnKind, NapiImpl, NapiStruct, NapiStructKind, TryToTokens,
12};
13use crate::{NapiArray, NapiClass, NapiObject, NapiStructuredEnum, NapiTransparent};
14
15static NAPI_IMPL_ID: AtomicU32 = AtomicU32::new(0);
16
17const STRUCT_FIELD_SPECIAL_CASE: &[&str] = &["Option", "Result"];
18
19#[cfg(feature = "tracing")]
20fn gen_tracing_debug(class_name: &str, method_name: &str) -> TokenStream {
21 let full_name = format!("{}::{}", class_name, method_name);
22 quote! {
23 napi::bindgen_prelude::trace_napi_call(#full_name);
24 }
25}
26
27#[cfg(not(feature = "tracing"))]
28fn gen_tracing_debug(_class_name: &str, _method_name: &str) -> TokenStream {
29 quote! {}
30}
31
32fn gen_napi_value_map_impl(
40 name: &Ident,
41 js_name: &str,
42 to_napi_val_impl: TokenStream,
43 has_lifetime: bool,
44 type_tag: Option<&str>,
45) -> TokenStream {
46 let name_str = name.to_string();
47 let type_tag_body = match type_tag {
61 Some(salt) => {
62 let salt_lit = Literal::string(salt);
63 quote! {
64 napi::bindgen_prelude::type_tag_from_ident(concat!(
71 #salt_lit, "::", module_path!(), "::", #name_str
72 ))
73 }
74 }
75 None => quote! {
76 napi::bindgen_prelude::type_tag_from_ident(concat!(
82 env!("CARGO_PKG_NAME"), "@", env!("CARGO_PKG_VERSION"),
83 "::", module_path!(), "::", #name_str
84 ))
85 },
86 };
87 let name = if has_lifetime {
88 quote! { #name<'_> }
89 } else {
90 quote! { #name }
91 };
92 let js_name_str = format!("{js_name}\0");
93 let validate = quote! {
94 unsafe fn validate(env: napi::sys::napi_env, napi_val: napi::sys::napi_value) -> napi::Result<napi::sys::napi_value> {
95 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
96 let mut ctor = std::ptr::null_mut();
97 napi::check_status!(
98 napi::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
99 "Failed to get constructor reference of class `{}`",
100 #name_str
101 )?;
102 let mut is_instance_of = false;
103 napi::check_status!(
104 napi::sys::napi_instanceof(env, napi_val, ctor, &mut is_instance_of),
105 "Failed to get external value of class `{}`",
106 #name_str
107 )?;
108 if is_instance_of {
109 Ok(std::ptr::null_mut())
110 } else {
111 Err(napi::Error::new(
112 napi::Status::InvalidArg,
113 format!("Value is not instanceof class `{}`", #name_str)
114 ))
115 }
116 } else {
117 Err(napi::Error::new(
118 napi::Status::InvalidArg,
119 format!("Failed to get constructor of class `{}`", #name_str)
120 ))
121 }
122 }
123 };
124 quote! {
125 #[automatically_derived]
126 impl napi::bindgen_prelude::TypeName for #name {
127 fn type_name() -> &'static str {
128 #name_str
129 }
130
131 fn value_type() -> napi::ValueType {
132 napi::ValueType::Function
133 }
134 }
135
136 #[automatically_derived]
137 impl napi::bindgen_prelude::TypeName for &#name {
138 fn type_name() -> &'static str {
139 #name_str
140 }
141
142 fn value_type() -> napi::ValueType {
143 napi::ValueType::Object
144 }
145 }
146
147 #[automatically_derived]
148 impl napi::bindgen_prelude::TypeName for &mut #name {
149 fn type_name() -> &'static str {
150 #name_str
151 }
152
153 fn value_type() -> napi::ValueType {
154 napi::ValueType::Object
155 }
156 }
157
158 #to_napi_val_impl
159
160 #[automatically_derived]
161 impl napi::bindgen_prelude::TypeTag for #name {
162 fn type_tag() -> napi::bindgen_prelude::sys::napi_type_tag {
163 #type_tag_body
164 }
165 }
166
167 #[automatically_derived]
168 impl napi::bindgen_prelude::FromNapiRef for #name {
169 unsafe fn from_napi_ref(
170 env: napi::bindgen_prelude::sys::napi_env,
171 napi_val: napi::bindgen_prelude::sys::napi_value
172 ) -> napi::bindgen_prelude::Result<&'static Self> {
173 let mut wrapped_val: *mut std::ffi::c_void = std::ptr::null_mut();
174
175 napi::bindgen_prelude::check_status!(
176 napi::bindgen_prelude::sys::napi_unwrap(env, napi_val, &mut wrapped_val),
177 "Failed to recover `{}` type from napi value",
178 #name_str,
179 )?;
180
181 napi::bindgen_prelude::validate_type_tag(
187 env,
188 napi_val,
189 &<#name as napi::bindgen_prelude::TypeTag>::type_tag(),
190 #name_str,
191 )?;
192
193 napi::bindgen_prelude::register_native_borrow_with_value(
196 env,
197 napi_val,
198 wrapped_val.cast::<#name>(),
199 false,
200 )?;
201
202 Ok(&*(wrapped_val as *const #name))
203 }
204 }
205
206 #[automatically_derived]
207 impl napi::bindgen_prelude::FromNapiMutRef for #name {
208 unsafe fn from_napi_mut_ref(
209 env: napi::bindgen_prelude::sys::napi_env,
210 napi_val: napi::bindgen_prelude::sys::napi_value
211 ) -> napi::bindgen_prelude::Result<&'static mut Self> {
212 let mut wrapped_val: *mut std::ffi::c_void = std::ptr::null_mut();
213
214 napi::bindgen_prelude::check_status!(
215 napi::bindgen_prelude::sys::napi_unwrap(env, napi_val, &mut wrapped_val),
216 "Failed to recover `{}` type from napi value",
217 #name_str,
218 )?;
219
220 napi::bindgen_prelude::validate_type_tag(
226 env,
227 napi_val,
228 &<#name as napi::bindgen_prelude::TypeTag>::type_tag(),
229 #name_str,
230 )?;
231
232 napi::bindgen_prelude::register_native_borrow_with_value(
235 env,
236 napi_val,
237 wrapped_val.cast::<#name>(),
238 true,
239 )?;
240
241 Ok(&mut *(wrapped_val as *mut #name))
242 }
243 }
244
245 #[automatically_derived]
246 impl napi::bindgen_prelude::ValidateNapiValue for &#name {
247 #validate
248 }
249
250 #[automatically_derived]
251 impl napi::bindgen_prelude::ValidateNapiValue for &mut #name {
252 #validate
253 }
254 }
255}
256
257fn is_option_type(ty: &syn::Type) -> bool {
258 if let syn::Type::Path(syn::TypePath {
259 path: syn::Path { segments, .. },
260 ..
261 }) = ty
262 {
263 matches!(segments.last(), Some(last_path) if last_path.ident == "Option")
264 } else {
265 false
266 }
267}
268
269fn gen_field_name_c_string(field_js_name: &str) -> TokenStream {
270 if field_js_name.contains('\0') {
271 return quote! {
272 compile_error!("napi object field names and structured enum discriminants cannot contain NUL bytes")
273 };
274 }
275
276 let field_js_name_lit = Literal::string(&format!("{field_js_name}\0"));
277 quote! {
278 std::ffi::CStr::from_bytes_with_nul_unchecked(#field_js_name_lit.as_bytes()).as_ptr()
279 }
280}
281
282fn gen_named_property_descriptor(
283 field_name_c_string: &TokenStream,
284 value_var: &Ident,
285) -> TokenStream {
286 quote! {
287 napi::bindgen_prelude::sys::napi_property_descriptor {
288 utf8name: #field_name_c_string,
289 name: std::ptr::null_mut(),
290 method: None,
291 getter: None,
292 setter: None,
293 value: #value_var,
294 attributes: napi::bindgen_prelude::sys::PropertyAttributes::writable
295 | napi::bindgen_prelude::sys::PropertyAttributes::enumerable
296 | napi::bindgen_prelude::sys::PropertyAttributes::configurable,
297 data: std::ptr::null_mut(),
298 }
299 }
300}
301
302#[allow(clippy::too_many_arguments)]
303fn gen_raw_field_getter(
304 value_ident: &Ident,
305 raw_ident: &Ident,
306 ty: &syn::Type,
307 field_js_name: &str,
308 field_name_c_string: &TokenStream,
309 struct_name: &str,
310 is_optional_field: bool,
311 use_nullable: bool,
312 obj_raw: TokenStream,
313) -> TokenStream {
314 let read_helper = if is_optional_field && !use_nullable {
315 quote! { from_raw_optional_field }
316 } else {
317 quote! { from_raw_required_field }
318 };
319
320 quote! {
321 let #raw_ident = napi::bindgen_prelude::get_named_property_raw(env, #obj_raw, #field_name_c_string)?;
322 let #value_ident: #ty = napi::bindgen_prelude::#read_helper(env, #raw_ident, #struct_name, #field_js_name)?;
323 }
324}
325
326fn gen_optional_conditional_setter(
327 value_ident: &Ident,
328 field_name_c_string: &TokenStream,
329 obj_raw: TokenStream,
330) -> TokenStream {
331 quote! {
332 if let Some(inner) = #value_ident {
333 let value = napi::bindgen_prelude::ToNapiValue::to_napi_value(env, inner)?;
334 napi::bindgen_prelude::set_named_property_raw(env, #obj_raw, #field_name_c_string, value)?;
335 }
336 }
337}
338
339impl TryToTokens for NapiStruct {
340 fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
341 let napi_value_map_impl = self.gen_napi_value_map_impl();
342
343 let class_helper_mod = match &self.kind {
344 NapiStructKind::Class(class) => self.gen_helper_mod(class),
345 _ => quote! {},
346 };
347
348 (quote! {
349 #napi_value_map_impl
350 #class_helper_mod
351 })
352 .to_tokens(tokens);
353
354 Ok(())
355 }
356}
357
358impl NapiStruct {
359 fn gen_helper_mod(&self, class: &NapiClass) -> TokenStream {
360 let mod_name = Ident::new(&format!("__napi_helper__{}", self.name), Span::call_site());
361
362 let ctor = if class.ctor {
363 self.gen_default_ctor(class)
364 } else {
365 quote! {}
366 };
367
368 let mut getters_setters = self.gen_default_getters_setters(class);
369 getters_setters.sort_by(|a, b| a.0.cmp(&b.0));
370 let register = self.gen_register(class);
371
372 let getters_setters_token = getters_setters.into_iter().map(|(_, token)| token);
373
374 quote! {
375 #[allow(clippy::all)]
376 #[allow(non_snake_case)]
377 mod #mod_name {
378 use std::ptr;
379 use super::*;
380
381 #ctor
382 #(#getters_setters_token)*
383 #register
384 }
385 }
386 }
387
388 fn gen_default_ctor(&self, class: &NapiClass) -> TokenStream {
389 let name = &self.name;
390 let js_name_str = &self.js_name;
391 let fields_len = class.fields.len();
392 let mut fields = vec![];
393
394 for (i, field) in class.fields.iter().enumerate() {
395 let ty = &field.ty;
396 match &field.name {
397 syn::Member::Named(ident) => fields
398 .push(quote! { #ident: <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#i))? }),
399 syn::Member::Unnamed(_) => {
400 fields.push(quote! { <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#i))? });
401 }
402 }
403 }
404
405 let construct = if class.is_tuple {
406 quote! { #name (#(#fields),*) }
407 } else {
408 quote! { #name {#(#fields),*} }
409 };
410
411 let is_empty_struct_hint = fields_len == 0;
412
413 let constructor = if class.implement_iterator {
414 quote! { unsafe { cb.construct_generator::<#is_empty_struct_hint, #name>(#js_name_str, #construct) } }
415 } else {
416 quote! { unsafe { cb.construct::<#is_empty_struct_hint, #name>(#js_name_str, #construct) } }
417 };
418
419 let tracing_debug = gen_tracing_debug(js_name_str, "constructor");
420
421 quote! {
422 extern "C" fn constructor(
423 env: napi::bindgen_prelude::sys::napi_env,
424 cb: napi::bindgen_prelude::sys::napi_callback_info
425 ) -> napi::bindgen_prelude::sys::napi_value {
426 #tracing_debug
427 napi::bindgen_prelude::CallbackInfo::<#fields_len>::new(env, cb, None, false)
428 .and_then(|cb| #constructor)
429 .unwrap_or_else(|e| {
430 unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
431 std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
432 })
433 }
434 }
435 }
436
437 fn gen_napi_value_map_impl(&self) -> TokenStream {
438 match &self.kind {
439 NapiStructKind::Array(array) => self.gen_napi_value_array_impl(array),
440 NapiStructKind::Transparent(transparent) => self.gen_napi_value_transparent_impl(transparent),
441 NapiStructKind::Class(class) if !class.ctor => gen_napi_value_map_impl(
442 &self.name,
443 &self.js_name,
444 self.gen_to_napi_value_ctor_impl_for_non_default_constructor_struct(class),
445 self.has_lifetime,
446 self.type_tag.as_deref(),
447 ),
448 NapiStructKind::Class(class) => gen_napi_value_map_impl(
449 &self.name,
450 &self.js_name,
451 self.gen_to_napi_value_ctor_impl(class),
452 self.has_lifetime,
453 self.type_tag.as_deref(),
454 ),
455 NapiStructKind::Object(obj) => self.gen_to_napi_value_obj_impl(obj),
456 NapiStructKind::StructuredEnum(structured_enum) => {
457 self.gen_to_napi_value_structured_enum_impl(structured_enum)
458 }
459 }
460 }
461
462 fn gen_to_napi_value_ctor_impl_for_non_default_constructor_struct(
463 &self,
464 class: &NapiClass,
465 ) -> TokenStream {
466 let name = &self.name;
467 let js_name_raw = &self.js_name;
468 let js_name_str = format!("{js_name_raw}\0");
469 let iterator_implementation = self.gen_iterator_property(class, name);
470 let async_iterator_implementation = self.gen_async_iterator_property(class, name);
471 let (object_finalize_impl, to_napi_value_impl, javascript_class_ext_impl) = if self.has_lifetime
472 {
473 let name = quote! { #name<'_javascript_function_scope> };
474 (
475 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ObjectFinalize for #name {} },
476 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name },
477 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::JavaScriptClassExt for #name },
478 )
479 } else {
480 (
481 quote! { impl napi::bindgen_prelude::ObjectFinalize for #name {} },
482 quote! { impl napi::bindgen_prelude::ToNapiValue for #name },
483 quote! { impl napi::bindgen_prelude::JavaScriptClassExt for #name },
484 )
485 };
486 let finalize_trait = if class.use_custom_finalize {
487 quote! {}
488 } else {
489 quote! {
490 #[automatically_derived]
491 #object_finalize_impl
492 }
493 };
494 quote! {
495 #[automatically_derived]
496 #to_napi_value_impl {
497 unsafe fn to_napi_value(
498 env: napi::sys::napi_env,
499 val: #name
500 ) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
501 if let Some(ctor_ref) = napi::__private::get_class_constructor(#js_name_str) {
502 let mut wrapped_value = Box::into_raw(Box::new(val));
503 if wrapped_value as usize == 0x1 {
504 wrapped_value = Box::into_raw(Box::new(0u8)).cast();
505 }
506 let instance_value = napi::bindgen_prelude::new_instance::<#name>(env, wrapped_value.cast(), ctor_ref)?;
507 #iterator_implementation
508 #async_iterator_implementation
509 Ok(instance_value)
510 } else {
511 Err(napi::bindgen_prelude::Error::new(
512 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}` in `ToNapiValue`", #js_name_raw))
513 )
514 }
515 }
516 }
517
518 #finalize_trait
519
520 #[automatically_derived]
521 #javascript_class_ext_impl {
522 fn into_instance<'scope>(self, env: &'scope napi::Env) -> napi::Result<napi::bindgen_prelude::ClassInstance<'scope, Self>>
523 {
524 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
525 unsafe {
526 let mut wrapped_value = Box::into_raw(Box::new(self));
527 if wrapped_value as usize == 0x1 {
528 wrapped_value = Box::into_raw(Box::new(0u8)).cast();
529 }
530 let instance_value = napi::bindgen_prelude::new_instance::<#name>(env.raw(), wrapped_value.cast(), ctor_ref)?;
531 Ok(napi::bindgen_prelude::ClassInstance::new(instance_value, env.raw(), wrapped_value))
532 }
533 } else {
534 Err(napi::bindgen_prelude::Error::new(
535 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
536 )
537 }
538 }
539
540 fn into_reference(self, env: napi::Env) -> napi::Result<napi::bindgen_prelude::Reference<Self>> {
541 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
542 unsafe {
543 let mut wrapped_value = Box::into_raw(Box::new(self));
544 if wrapped_value as usize == 0x1 {
545 wrapped_value = Box::into_raw(Box::new(0u8)).cast();
546 }
547 let instance_value = napi::bindgen_prelude::new_instance::<#name>(env.raw(), wrapped_value.cast(), ctor_ref)?;
548 {
549 let env = env.raw();
550 #iterator_implementation
551 #async_iterator_implementation
552 }
553 napi::bindgen_prelude::Reference::<#name>::from_value_ptr(wrapped_value.cast(), env.raw())
554 }
555 } else {
556 Err(napi::bindgen_prelude::Error::new(
557 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
558 )
559 }
560 }
561
562 fn instance_of<'env, V: napi::JsValue<'env>>(env: &napi::bindgen_prelude::Env, value: &V) -> napi::bindgen_prelude::Result<bool> {
563 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
564 let mut ctor = std::ptr::null_mut();
565 napi::check_status!(
566 unsafe { napi::sys::napi_get_reference_value(env.raw(), ctor_ref, &mut ctor) },
567 "Failed to get constructor reference of class `{}`",
568 #js_name_str
569 )?;
570 let mut is_instance_of = false;
571 napi::check_status!(
572 unsafe { napi::sys::napi_instanceof(env.raw(), value.value().value, ctor, &mut is_instance_of) },
573 "Failed to run instanceof for class `{}`",
574 #js_name_str
575 )?;
576 Ok(is_instance_of)
577 } else {
578 Err(napi::Error::new(napi::Status::GenericFailure, format!("Failed to get constructor of class `{}`", #js_name_str)))
579 }
580 }
581 }
582 }
583 }
584
585 fn gen_iterator_property(&self, class: &NapiClass, name: &Ident) -> TokenStream {
586 if !class.implement_iterator {
587 return quote! {};
588 }
589 quote! {
590 unsafe { napi::__private::create_iterator::<#name>(env, instance_value, wrapped_value); }
591 }
592 }
593
594 fn gen_async_iterator_property(&self, class: &NapiClass, name: &Ident) -> TokenStream {
595 if !class.implement_async_iterator {
596 return quote! {};
597 }
598 quote! {
604 napi::__private::create_async_iterator::<#name>(env, instance_value, wrapped_value);
605 }
606 }
607
608 fn gen_to_napi_value_ctor_impl(&self, class: &NapiClass) -> TokenStream {
609 let name = &self.name;
610 let js_name_without_null = &self.js_name;
611 let js_name_str = format!("{}\0", self.js_name);
612
613 let mut field_conversions = vec![];
614 let mut field_destructions = vec![];
615
616 for field in class.fields.iter() {
617 let ty = &field.ty;
618
619 match &field.name {
620 syn::Member::Named(ident) => {
621 let alias_ident = format_ident!("{}_", ident);
623 field_destructions.push(quote! { #ident: #alias_ident });
624 field_conversions.push(
625 quote! { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #alias_ident)? },
626 );
627 }
628 syn::Member::Unnamed(i) => {
629 let arg_name = format_ident!("arg{}", i);
630 field_destructions.push(quote! { #arg_name });
631 field_conversions.push(
632 quote! { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #arg_name)? },
633 );
634 }
635 }
636 }
637
638 let destructed_fields = if class.is_tuple {
639 quote! {
640 Self (#(#field_destructions),*)
641 }
642 } else {
643 quote! {
644 Self {#(#field_destructions),*}
645 }
646 };
647
648 let finalize_trait = if class.use_custom_finalize {
649 quote! {}
650 } else if self.has_lifetime {
651 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ObjectFinalize for #name<'_javascript_function_scope> {} }
652 } else {
653 quote! { impl napi::bindgen_prelude::ObjectFinalize for #name {} }
654 };
655
656 let to_napi_value_impl = if self.has_lifetime {
657 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name<'_javascript_function_scope> }
658 } else {
659 quote! { impl napi::bindgen_prelude::ToNapiValue for #name }
660 };
661
662 quote! {
663 #[automatically_derived]
664 #to_napi_value_impl {
665 unsafe fn to_napi_value(
666 env: napi::bindgen_prelude::sys::napi_env,
667 val: #name,
668 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
669 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
670 let mut ctor = std::ptr::null_mut();
671
672 napi::bindgen_prelude::check_status!(
673 napi::bindgen_prelude::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
674 "Failed to get constructor reference of class `{}`",
675 #js_name_without_null
676 )?;
677
678 let mut instance_value = std::ptr::null_mut();
679 let #destructed_fields = val;
680 let args = vec![#(#field_conversions),*];
681
682 napi::bindgen_prelude::check_status!(
683 napi::bindgen_prelude::sys::napi_new_instance(env, ctor, args.len(), args.as_ptr(), &mut instance_value),
684 "Failed to construct class `{}`",
685 #js_name_without_null
686 )?;
687
688 Ok(instance_value)
689 } else {
690 Err(napi::bindgen_prelude::Error::new(
691 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_str))
692 )
693 }
694 }
695 }
696 #finalize_trait
697 }
698 }
699
700 fn gen_to_napi_value_obj_impl(&self, obj: &NapiObject) -> TokenStream {
701 let name = &self.name;
702 let name_str = self.name.to_string();
703
704 let mut obj_field_getters = vec![];
705 let mut field_destructions = vec![];
706
707 let mut value_conversions = vec![];
709 let mut property_descriptors = vec![];
710 let mut conditional_setters = vec![];
711 let mut value_names = vec![];
712
713 for (idx, field) in obj.fields.iter().enumerate() {
714 let field_js_name = &field.js_name;
715 let field_name_c_string = gen_field_name_c_string(field_js_name);
716 let mut ty = field.ty.clone();
717 remove_lifetime_in_type(&mut ty);
718 let is_optional_field = is_option_type(&ty);
719
720 let is_always_set = !is_optional_field || self.use_nullable;
722
723 match &field.name {
724 syn::Member::Named(ident) => {
725 let alias_ident = format_ident!("{}_", ident);
726 field_destructions.push(quote! { #ident: #alias_ident });
727
728 if is_always_set {
729 let value_var = Ident::new(&format!("__obj_value_{}", idx), Span::call_site());
731 value_names.push(value_var.clone());
732
733 if is_optional_field {
734 value_conversions.push(quote! {
736 let #value_var = if let Some(inner) = #alias_ident {
737 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, inner)?
738 } else {
739 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, napi::bindgen_prelude::Null)?
740 };
741 });
742 } else {
743 value_conversions.push(quote! {
745 let #value_var = napi::bindgen_prelude::ToNapiValue::to_napi_value(env, #alias_ident)?;
746 });
747 }
748
749 property_descriptors.push(gen_named_property_descriptor(
750 &field_name_c_string,
751 &value_var,
752 ));
753 } else {
754 conditional_setters.push(gen_optional_conditional_setter(
756 &alias_ident,
757 &field_name_c_string,
758 quote! { obj_ptr },
759 ));
760 }
761
762 let raw_ident = Ident::new(&format!("__obj_field_raw_{}", idx), Span::call_site());
763 obj_field_getters.push(gen_raw_field_getter(
764 &alias_ident,
765 &raw_ident,
766 &ty,
767 field_js_name,
768 &field_name_c_string,
769 &name_str,
770 is_optional_field,
771 self.use_nullable,
772 quote! { napi::bindgen_prelude::JsValue::raw(&obj) },
773 ));
774 }
775 syn::Member::Unnamed(i) => {
776 let arg_name = format_ident!("arg{}", i);
777 field_destructions.push(quote! { #arg_name });
778
779 if is_always_set {
780 let value_var = Ident::new(&format!("__obj_value_{}", idx), Span::call_site());
782 value_names.push(value_var.clone());
783
784 if is_optional_field {
785 value_conversions.push(quote! {
787 let #value_var = if let Some(inner) = #arg_name {
788 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, inner)?
789 } else {
790 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, napi::bindgen_prelude::Null)?
791 };
792 });
793 } else {
794 value_conversions.push(quote! {
796 let #value_var = napi::bindgen_prelude::ToNapiValue::to_napi_value(env, #arg_name)?;
797 });
798 }
799
800 property_descriptors.push(gen_named_property_descriptor(
801 &field_name_c_string,
802 &value_var,
803 ));
804 } else {
805 conditional_setters.push(gen_optional_conditional_setter(
807 &arg_name,
808 &field_name_c_string,
809 quote! { obj_ptr },
810 ));
811 }
812
813 let raw_ident = Ident::new(&format!("__obj_field_raw_{}", idx), Span::call_site());
814 obj_field_getters.push(gen_raw_field_getter(
815 &arg_name,
816 &raw_ident,
817 &ty,
818 field_js_name,
819 &field_name_c_string,
820 "",
821 is_optional_field,
822 self.use_nullable,
823 quote! { napi::bindgen_prelude::JsValue::raw(&obj) },
824 ));
825 }
826 }
827 }
828
829 let destructed_fields = if obj.is_tuple {
830 quote! {
831 Self (#(#field_destructions),*)
832 }
833 } else {
834 quote! {
835 Self {#(#field_destructions),*}
836 }
837 };
838
839 let name_with_lifetime = if self.has_lifetime {
840 quote! { #name<'_javascript_function_scope> }
841 } else {
842 quote! { #name }
843 };
844 let (from_napi_value_impl, to_napi_value_impl, validate_napi_value_impl, type_name_impl) =
845 if self.has_lifetime {
846 (
847 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::FromNapiValue for #name<'_javascript_function_scope> },
848 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name<'_javascript_function_scope> },
849 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ValidateNapiValue for #name<'_javascript_function_scope> },
850 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::TypeName for #name<'_javascript_function_scope> },
851 )
852 } else {
853 (
854 quote! { impl napi::bindgen_prelude::FromNapiValue for #name },
855 quote! { impl napi::bindgen_prelude::ToNapiValue for #name },
856 quote! { impl napi::bindgen_prelude::ValidateNapiValue for #name },
857 quote! { impl napi::bindgen_prelude::TypeName for #name },
858 )
859 };
860
861 let object_creation = if conditional_setters.is_empty() {
863 quote! {
865 #(#value_conversions)*
867
868 let properties = [
869 #(#property_descriptors),*
870 ];
871
872 let obj_ptr = napi::bindgen_prelude::create_object_with_properties(env, &properties)?;
873 Ok(obj_ptr)
874 }
875 } else {
876 quote! {
878 #(#value_conversions)*
880
881 let properties = [
882 #(#property_descriptors),*
883 ];
884
885 let obj_ptr = napi::bindgen_prelude::create_object_with_properties(env, &properties)?;
886
887 #(#conditional_setters)*
888
889 Ok(obj_ptr)
890 }
891 };
892
893 let to_napi_value = if obj.object_to_js {
894 quote! {
895 #[automatically_derived]
896 #to_napi_value_impl {
897 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> {
898 let #destructed_fields = val;
899 #object_creation
900 }
901 }
902 }
903 } else {
904 quote! {}
905 };
906
907 let from_napi_value = if obj.object_from_js {
908 let return_type = if self.has_lifetime {
909 quote! { #name<'_javascript_function_scope> }
910 } else {
911 quote! { #name }
912 };
913 quote! {
914 #[automatically_derived]
915 #from_napi_value_impl {
916 unsafe fn from_napi_value(
917 env: napi::bindgen_prelude::sys::napi_env,
918 napi_val: napi::bindgen_prelude::sys::napi_value
919 ) -> napi::bindgen_prelude::Result<#return_type> {
920 #[allow(unused_variables)]
921 let env_wrapper = napi::bindgen_prelude::Env::from(env);
922 #[allow(unused_mut)]
923 let mut obj = napi::bindgen_prelude::Object::from_napi_value(env, napi_val)?;
924
925 #(#obj_field_getters)*
926
927 let val = #destructed_fields;
928
929 Ok(val)
930 }
931 }
932
933 #[automatically_derived]
934 #validate_napi_value_impl {}
935 }
936 } else {
937 quote! {}
938 };
939
940 quote! {
941 #[automatically_derived]
942 #type_name_impl {
943 fn type_name() -> &'static str {
944 #name_str
945 }
946
947 fn value_type() -> napi::ValueType {
948 napi::ValueType::Object
949 }
950 }
951
952 #to_napi_value
953
954 #from_napi_value
955 }
956 }
957
958 fn gen_default_getters_setters(&self, class: &NapiClass) -> Vec<(String, TokenStream)> {
959 let mut getters_setters = vec![];
960 let struct_name = &self.name;
961 let js_name_str = &self.js_name;
962
963 for field in class.fields.iter() {
964 let field_ident = &field.name;
965 let field_name = match &field.name {
966 syn::Member::Named(ident) => ident.to_string(),
967 syn::Member::Unnamed(i) => format!("field{}", i.index),
968 };
969 let ty = &field.ty;
970
971 let getter_name = Ident::new(
972 &format!("get_{}", rm_raw_prefix(&field_name)),
973 Span::call_site(),
974 );
975 let setter_name = Ident::new(
976 &format!("set_{}", rm_raw_prefix(&field_name)),
977 Span::call_site(),
978 );
979 let accessor_descriptor_name = Ident::new(
980 &format!(
981 "__napi_field_accessor_descriptor_{}",
982 rm_raw_prefix(&field_name)
983 ),
984 Span::call_site(),
985 );
986
987 if field.getter {
988 let default_to_napi_value_convert = quote! {
989 let val = &mut obj.#field_ident;
990 unsafe { <&mut #ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) }
991 };
992 let to_napi_value_convert = if let syn::Type::Path(syn::TypePath {
993 path: syn::Path { segments, .. },
994 ..
995 }) = ty
996 {
997 if let Some(syn::PathSegment { ident, .. }) = segments.last() {
998 if STRUCT_FIELD_SPECIAL_CASE.iter().any(|name| ident == name) {
999 quote! {
1000 let val = obj.#field_ident.as_mut();
1001 unsafe { napi::bindgen_prelude::ToNapiValue::to_napi_value(env, val) }
1002 }
1003 } else {
1004 default_to_napi_value_convert
1005 }
1006 } else {
1007 default_to_napi_value_convert
1008 }
1009 } else {
1010 default_to_napi_value_convert
1011 };
1012 let tracing_debug = gen_tracing_debug(js_name_str, &field.js_name);
1013 getters_setters.push((
1014 field.js_name.clone(),
1015 quote! {
1016 unsafe fn #getter_name(
1017 env: napi::bindgen_prelude::sys::napi_env,
1018 this: napi::bindgen_prelude::sys::napi_value
1019 ) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
1020 #tracing_debug
1021 let this_ptr = unsafe {
1022 napi::bindgen_prelude::class_accessor_unwrap_this::<#struct_name>(env, this)?
1023 };
1024 let _napi_native_borrow =
1027 napi::bindgen_prelude::acquire_native_borrow(this_ptr, true)?;
1028 let obj: &mut #struct_name = Box::leak(unsafe { Box::from_raw(this_ptr) });
1029 #to_napi_value_convert
1030 }
1031 },
1032 ));
1033 }
1034
1035 if field.setter {
1036 let setter_tracing_debug =
1037 gen_tracing_debug(js_name_str, &format!("set_{}", field.js_name));
1038 getters_setters.push((
1039 field.js_name.clone(),
1040 quote! {
1041 unsafe fn #setter_name(
1042 env: napi::bindgen_prelude::sys::napi_env,
1043 this: napi::bindgen_prelude::sys::napi_value,
1044 value: napi::bindgen_prelude::sys::napi_value
1045 ) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
1046 #setter_tracing_debug
1047 let val = unsafe {
1048 <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, value)?
1049 };
1050 let this_ptr = unsafe {
1051 napi::bindgen_prelude::class_accessor_unwrap_this::<#struct_name>(env, this)?
1052 };
1053 let _napi_native_borrow =
1055 napi::bindgen_prelude::acquire_native_borrow(this_ptr, true)?;
1056 let obj: &mut #struct_name = Box::leak(unsafe { Box::from_raw(this_ptr) });
1057 obj.#field_ident = val;
1058 unsafe { <() as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, ()) }
1059 }
1060 },
1061 ));
1062 }
1063
1064 if field.getter {
1065 let getter = quote! { Some(#getter_name) };
1066 let setter = if field.setter {
1067 quote! { Some(#setter_name) }
1068 } else {
1069 quote! { None }
1070 };
1071 getters_setters.push((
1072 field.js_name.clone(),
1073 quote! {
1074 static #accessor_descriptor_name: napi::bindgen_prelude::ClassAccessorDescriptor = napi::bindgen_prelude::ClassAccessorDescriptor {
1075 getter: #getter,
1076 setter: #setter,
1077 };
1078 },
1079 ));
1080 }
1081 }
1082
1083 getters_setters
1084 }
1085
1086 fn gen_register(&self, class: &NapiClass) -> TokenStream {
1087 let name = &self.name;
1088 let struct_register_name = &self.register_name;
1089 let js_name = format!("{}\0", self.js_name);
1090 let implement_iterator = class.implement_iterator;
1091 let mut props = vec![];
1092
1093 if class.ctor {
1094 props.push(quote! { napi::bindgen_prelude::Property::new().with_utf8_name("constructor").unwrap().with_ctor(constructor) });
1095 }
1096
1097 for field in class.fields.iter() {
1098 let field_name = match &field.name {
1099 syn::Member::Named(ident) => ident.to_string(),
1100 syn::Member::Unnamed(i) => format!("field{}", i.index),
1101 };
1102
1103 if !field.getter {
1104 continue;
1105 }
1106
1107 let js_name = &field.js_name;
1108 let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT;
1109 if field.writable {
1110 attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE;
1111 }
1112 if field.enumerable {
1113 attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE;
1114 }
1115 if field.configurable {
1116 attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE;
1117 }
1118
1119 let property = gen_member_property(js_name, &field.symbol_for, &field.well_known_symbol);
1120 let mut prop = quote! {
1121 #property
1122 .with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap())
1123 };
1124
1125 if field.getter {
1126 let accessor_descriptor_name = Ident::new(
1127 &format!(
1128 "__napi_field_accessor_descriptor_{}",
1129 rm_raw_prefix(&field_name)
1130 ),
1131 Span::call_site(),
1132 );
1133 (quote! {
1134 .with_getter(napi::bindgen_prelude::class_getter_trampoline)
1135 .with_data(&#accessor_descriptor_name as *const _ as *mut _)
1136 })
1137 .to_tokens(&mut prop);
1138 }
1139
1140 if field.writable && field.setter {
1141 let accessor_descriptor_name = Ident::new(
1142 &format!(
1143 "__napi_field_accessor_descriptor_{}",
1144 rm_raw_prefix(&field_name)
1145 ),
1146 Span::call_site(),
1147 );
1148 (quote! {
1149 .with_setter(napi::bindgen_prelude::class_setter_trampoline)
1150 .with_data(&#accessor_descriptor_name as *const _ as *mut _)
1151 })
1152 .to_tokens(&mut prop);
1153 }
1154
1155 props.push(prop);
1156 }
1157 let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
1158 quote! {
1159 #[cfg(all(not(test), not(target_family = "wasm")))]
1160 napi::ctor::declarative::ctor! {
1161 #[allow(non_snake_case)]
1162 #[allow(clippy::all)]
1163 #[ctor(unsafe)]
1164 fn #struct_register_name() {
1165 napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*], #implement_iterator);
1166 }
1167 }
1168
1169 #[allow(non_snake_case)]
1170 #[allow(clippy::all)]
1171 #[cfg(all(not(test), target_family = "wasm"))]
1172 #[no_mangle]
1173 extern "C" fn #struct_register_name() {
1174 napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*], #implement_iterator);
1175 }
1176 }
1177 }
1178
1179 fn gen_to_napi_value_structured_enum_impl(
1180 &self,
1181 structured_enum: &NapiStructuredEnum,
1182 ) -> TokenStream {
1183 let name = &self.name;
1184 let name_str = self.name.to_string();
1185 let discriminant = structured_enum.discriminant.as_str();
1186 let discriminant_c_string = gen_field_name_c_string(discriminant);
1187
1188 let mut variant_arm_setters = vec![];
1189 let mut variant_arm_getters = vec![];
1190
1191 for variant in structured_enum.variants.iter() {
1192 let variant_name = &variant.name;
1193 let mut variant_name_str = variant_name.to_string();
1194 if let Some(case) = structured_enum.discriminant_case {
1195 variant_name_str = to_case(variant_name_str, case);
1196 }
1197
1198 let mut obj_field_getters = vec![];
1199 let mut field_destructions = vec![];
1200
1201 let mut value_conversions = vec![];
1203 let mut property_descriptors = vec![];
1204 let mut conditional_setters = vec![];
1205
1206 let discriminant_value_var = Ident::new("__discriminant_value", Span::call_site());
1208 value_conversions.push(quote! {
1209 let #discriminant_value_var = napi::bindgen_prelude::ToNapiValue::to_napi_value(env, #variant_name_str)?;
1210 });
1211 property_descriptors.push(gen_named_property_descriptor(
1212 &discriminant_c_string,
1213 &discriminant_value_var,
1214 ));
1215
1216 for (idx, field) in variant.fields.iter().enumerate() {
1217 let field_js_name = &field.js_name;
1218 let field_name_c_string = gen_field_name_c_string(field_js_name);
1219 let mut ty = field.ty.clone();
1220 remove_lifetime_in_type(&mut ty);
1221 let is_optional_field = is_option_type(&ty);
1222
1223 let is_always_set = !is_optional_field || self.use_nullable;
1225
1226 match &field.name {
1227 syn::Member::Named(ident) => {
1228 let alias_ident = format_ident!("{}_", ident);
1229 field_destructions.push(quote! { #ident: #alias_ident });
1230
1231 if is_always_set {
1232 let value_var = Ident::new(&format!("__variant_value_{}", idx), Span::call_site());
1234
1235 if is_optional_field {
1236 value_conversions.push(quote! {
1238 let #value_var = if let Some(inner) = #alias_ident {
1239 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, inner)?
1240 } else {
1241 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, napi::bindgen_prelude::Null)?
1242 };
1243 });
1244 } else {
1245 value_conversions.push(quote! {
1247 let #value_var = napi::bindgen_prelude::ToNapiValue::to_napi_value(env, #alias_ident)?;
1248 });
1249 }
1250
1251 property_descriptors.push(gen_named_property_descriptor(
1252 &field_name_c_string,
1253 &value_var,
1254 ));
1255 } else {
1256 conditional_setters.push(gen_optional_conditional_setter(
1258 &alias_ident,
1259 &field_name_c_string,
1260 quote! { obj_ptr },
1261 ));
1262 }
1263
1264 let raw_ident = Ident::new(&format!("__variant_field_raw_{}", idx), Span::call_site());
1265 obj_field_getters.push(gen_raw_field_getter(
1266 &alias_ident,
1267 &raw_ident,
1268 &ty,
1269 field_js_name,
1270 &field_name_c_string,
1271 &name_str,
1272 is_optional_field,
1273 self.use_nullable,
1274 quote! { napi::bindgen_prelude::JsValue::raw(&obj) },
1275 ));
1276 }
1277 syn::Member::Unnamed(i) => {
1278 let arg_name = format_ident!("arg{}", i);
1279 field_destructions.push(quote! { #arg_name });
1280
1281 if is_always_set {
1282 let value_var = Ident::new(&format!("__variant_value_{}", idx), Span::call_site());
1284
1285 if is_optional_field {
1286 value_conversions.push(quote! {
1288 let #value_var = if let Some(inner) = #arg_name {
1289 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, inner)?
1290 } else {
1291 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, napi::bindgen_prelude::Null)?
1292 };
1293 });
1294 } else {
1295 value_conversions.push(quote! {
1297 let #value_var = napi::bindgen_prelude::ToNapiValue::to_napi_value(env, #arg_name)?;
1298 });
1299 }
1300
1301 property_descriptors.push(gen_named_property_descriptor(
1302 &field_name_c_string,
1303 &value_var,
1304 ));
1305 } else {
1306 conditional_setters.push(gen_optional_conditional_setter(
1308 &arg_name,
1309 &field_name_c_string,
1310 quote! { obj_ptr },
1311 ));
1312 }
1313
1314 let raw_ident = Ident::new(&format!("__variant_field_raw_{}", idx), Span::call_site());
1315 obj_field_getters.push(gen_raw_field_getter(
1316 &arg_name,
1317 &raw_ident,
1318 &ty,
1319 field_js_name,
1320 &field_name_c_string,
1321 "",
1322 is_optional_field,
1323 self.use_nullable,
1324 quote! { napi::bindgen_prelude::JsValue::raw(&obj) },
1325 ));
1326 }
1327 }
1328 }
1329
1330 let destructed_fields = if variant.is_tuple {
1331 quote! {
1332 Self::#variant_name (#(#field_destructions),*)
1333 }
1334 } else {
1335 quote! {
1336 Self::#variant_name {#(#field_destructions),*}
1337 }
1338 };
1339
1340 let variant_object_creation = if conditional_setters.is_empty() {
1342 quote! {
1344 #(#value_conversions)*
1345
1346 let properties = [
1347 #(#property_descriptors),*
1348 ];
1349
1350 napi::bindgen_prelude::create_object_with_properties(env, &properties)
1351 }
1352 } else {
1353 quote! {
1355 #(#value_conversions)*
1356
1357 let properties = [
1358 #(#property_descriptors),*
1359 ];
1360
1361 let obj_ptr = napi::bindgen_prelude::create_object_with_properties(env, &properties)?;
1362
1363 #(#conditional_setters)*
1364
1365 Ok(obj_ptr)
1366 }
1367 };
1368
1369 variant_arm_setters.push(quote! {
1370 #destructed_fields => {
1371 #variant_object_creation
1372 },
1373 });
1374
1375 variant_arm_getters.push(quote! {
1376 #variant_name_str => {
1377 #(#obj_field_getters)*
1378 #destructed_fields
1379 },
1380 })
1381 }
1382
1383 let to_napi_value = if structured_enum.object_to_js {
1384 quote! {
1385 impl napi::bindgen_prelude::ToNapiValue for #name {
1386 unsafe fn to_napi_value(env: napi::bindgen_prelude::sys::napi_env, val: #name) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
1387 match val {
1388 #(#variant_arm_setters)*
1389 }
1390 }
1391 }
1392 }
1393 } else {
1394 quote! {}
1395 };
1396
1397 let from_napi_value = if structured_enum.object_from_js {
1398 quote! {
1399 impl napi::bindgen_prelude::FromNapiValue for #name {
1400 unsafe fn from_napi_value(
1401 env: napi::bindgen_prelude::sys::napi_env,
1402 napi_val: napi::bindgen_prelude::sys::napi_value
1403 ) -> napi::bindgen_prelude::Result<Self> {
1404 #[allow(unused_variables)]
1405 let env_wrapper = napi::bindgen_prelude::Env::from(env);
1406 #[allow(unused_mut)]
1407 let mut obj = napi::bindgen_prelude::Object::from_napi_value(env, napi_val)?;
1408 let __discriminant_raw = napi::bindgen_prelude::get_named_property_raw(
1409 env,
1410 napi::bindgen_prelude::JsValue::raw(&obj),
1411 #discriminant_c_string,
1412 )?;
1413 let type_: String = napi::bindgen_prelude::from_raw_required_field(
1414 env,
1415 __discriminant_raw,
1416 #name_str,
1417 #discriminant,
1418 )?;
1419 let val = match type_.as_str() {
1420 #(#variant_arm_getters)*
1421 _ => return Err(napi::bindgen_prelude::Error::new(
1422 napi::bindgen_prelude::Status::InvalidArg,
1423 format!("Unknown variant `{}`", type_),
1424 )),
1425 };
1426
1427 Ok(val)
1428 }
1429 }
1430
1431 impl napi::bindgen_prelude::ValidateNapiValue for #name {}
1432 }
1433 } else {
1434 quote! {}
1435 };
1436
1437 quote! {
1438 impl napi::bindgen_prelude::TypeName for #name {
1439 fn type_name() -> &'static str {
1440 #name_str
1441 }
1442
1443 fn value_type() -> napi::ValueType {
1444 napi::ValueType::Object
1445 }
1446 }
1447
1448 #to_napi_value
1449
1450 #from_napi_value
1451 }
1452 }
1453
1454 fn gen_napi_value_transparent_impl(&self, transparent: &NapiTransparent) -> TokenStream {
1455 let name = &self.name;
1456 let name = if self.has_lifetime {
1457 quote! { #name<'_> }
1458 } else {
1459 quote! { #name }
1460 };
1461 let inner_type = transparent.ty.clone().into_token_stream();
1462
1463 let to_napi_value = if transparent.object_to_js {
1464 quote! {
1465 #[automatically_derived]
1466 impl napi::bindgen_prelude::ToNapiValue for #name {
1467 unsafe fn to_napi_value(
1468 env: napi::bindgen_prelude::sys::napi_env,
1469 val: Self
1470 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
1471 <#inner_type>::to_napi_value(env, val.0)
1472 }
1473 }
1474 }
1475 } else {
1476 quote! {}
1477 };
1478
1479 let from_napi_value = if transparent.object_from_js {
1480 quote! {
1481 #[automatically_derived]
1482 impl napi::bindgen_prelude::FromNapiValue for #name {
1483 unsafe fn from_napi_value(
1484 env: napi::bindgen_prelude::sys::napi_env,
1485 napi_val: napi::bindgen_prelude::sys::napi_value
1486 ) -> napi::bindgen_prelude::Result<Self> {
1487 Ok(Self(<#inner_type>::from_napi_value(env, napi_val)?))
1488 }
1489 }
1490 }
1491 } else {
1492 quote! {}
1493 };
1494
1495 quote! {
1496 #[automatically_derived]
1497 impl napi::bindgen_prelude::TypeName for #name {
1498 fn type_name() -> &'static str {
1499 <#inner_type>::type_name()
1500 }
1501
1502 fn value_type() -> napi::ValueType {
1503 <#inner_type>::value_type()
1504 }
1505 }
1506
1507 #[automatically_derived]
1508 impl napi::bindgen_prelude::ValidateNapiValue for #name {
1509 unsafe fn validate(
1510 env: napi::bindgen_prelude::sys::napi_env,
1511 napi_val: napi::bindgen_prelude::sys::napi_value
1512 ) -> napi::bindgen_prelude::Result<napi::sys::napi_value> {
1513 <#inner_type>::validate(env, napi_val)
1514 }
1515 }
1516
1517 #to_napi_value
1518
1519 #from_napi_value
1520 }
1521 }
1522
1523 fn gen_napi_value_array_impl(&self, array: &NapiArray) -> TokenStream {
1524 let name = &self.name;
1525 let name_str = self.name.to_string();
1526
1527 let mut obj_field_setters = vec![];
1528 let mut obj_field_getters = vec![];
1529 let mut field_destructions = vec![];
1530
1531 for field in array.fields.iter() {
1532 let mut ty = field.ty.clone();
1533 remove_lifetime_in_type(&mut ty);
1534 let is_optional_field = if let syn::Type::Path(syn::TypePath {
1535 path: syn::Path { segments, .. },
1536 ..
1537 }) = &ty
1538 {
1539 if let Some(last_path) = segments.last() {
1540 last_path.ident == "Option"
1541 } else {
1542 false
1543 }
1544 } else {
1545 false
1546 };
1547
1548 if let syn::Member::Unnamed(i) = &field.name {
1549 let arg_name = format_ident!("arg{}", i);
1550 let field_index = i.index;
1551 field_destructions.push(quote! { #arg_name });
1552 if is_optional_field {
1553 obj_field_setters.push(match self.use_nullable {
1554 false => quote! {
1555 if #arg_name.is_some() {
1556 array.set(#field_index, #arg_name)?;
1557 }
1558 },
1559 true => quote! {
1560 if let Some(#arg_name) = #arg_name {
1561 array.set(#field_index, #arg_name)?;
1562 } else {
1563 array.set(#field_index, napi::bindgen_prelude::Null)?;
1564 }
1565 },
1566 });
1567 } else {
1568 obj_field_setters.push(quote! { array.set(#field_index, #arg_name)?; });
1569 }
1570 if is_optional_field && !self.use_nullable {
1571 obj_field_getters.push(quote! { let #arg_name: #ty = array.get(#field_index)?; });
1572 } else {
1573 obj_field_getters.push(quote! {
1574 let #arg_name: #ty = array.get(#field_index)?.ok_or_else(|| napi::bindgen_prelude::Error::new(
1575 napi::bindgen_prelude::Status::InvalidArg,
1576 format!("Failed to get element with index `{}`", #field_index),
1577 ))?;
1578 });
1579 }
1580 }
1581 }
1582
1583 let destructed_fields = quote! {
1584 Self (#(#field_destructions),*)
1585 };
1586
1587 let name_with_lifetime = if self.has_lifetime {
1588 quote! { #name<'_javascript_function_scope> }
1589 } else {
1590 quote! { #name }
1591 };
1592 let (from_napi_value_impl, to_napi_value_impl, validate_napi_value_impl, type_name_impl) =
1593 if self.has_lifetime {
1594 (
1595 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::FromNapiValue for #name<'_javascript_function_scope> },
1596 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name<'_javascript_function_scope> },
1597 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ValidateNapiValue for #name<'_javascript_function_scope> },
1598 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::TypeName for #name<'_javascript_function_scope> },
1599 )
1600 } else {
1601 (
1602 quote! { impl napi::bindgen_prelude::FromNapiValue for #name },
1603 quote! { impl napi::bindgen_prelude::ToNapiValue for #name },
1604 quote! { impl napi::bindgen_prelude::ValidateNapiValue for #name },
1605 quote! { impl napi::bindgen_prelude::TypeName for #name },
1606 )
1607 };
1608
1609 let array_len = array.fields.len() as u32;
1610
1611 let to_napi_value = if array.object_to_js {
1612 quote! {
1613 #[automatically_derived]
1614 #to_napi_value_impl {
1615 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> {
1616 #[allow(unused_variables)]
1617 let env_wrapper = napi::bindgen_prelude::Env::from(env);
1618 #[allow(unused_mut)]
1619 let mut array = env_wrapper.create_array(#array_len)?;
1620
1621 let #destructed_fields = val;
1622 #(#obj_field_setters)*
1623
1624 napi::bindgen_prelude::Array::to_napi_value(env, array)
1625 }
1626 }
1627 }
1628 } else {
1629 quote! {}
1630 };
1631
1632 let from_napi_value = if array.object_from_js {
1633 let return_type = if self.has_lifetime {
1634 quote! { #name<'_javascript_function_scope> }
1635 } else {
1636 quote! { #name }
1637 };
1638 quote! {
1639 #[automatically_derived]
1640 #from_napi_value_impl {
1641 unsafe fn from_napi_value(
1642 env: napi::bindgen_prelude::sys::napi_env,
1643 napi_val: napi::bindgen_prelude::sys::napi_value
1644 ) -> napi::bindgen_prelude::Result<#return_type> {
1645 #[allow(unused_variables)]
1646 let env_wrapper = napi::bindgen_prelude::Env::from(env);
1647 #[allow(unused_mut)]
1648 let mut array = napi::bindgen_prelude::Array::from_napi_value(env, napi_val)?;
1649
1650 #(#obj_field_getters)*
1651
1652 let val = #destructed_fields;
1653
1654 Ok(val)
1655 }
1656 }
1657
1658 #[automatically_derived]
1659 #validate_napi_value_impl {}
1660 }
1661 } else {
1662 quote! {}
1663 };
1664
1665 quote! {
1666 #[automatically_derived]
1667 #type_name_impl {
1668 fn type_name() -> &'static str {
1669 #name_str
1670 }
1671
1672 fn value_type() -> napi::ValueType {
1673 napi::ValueType::Object
1674 }
1675 }
1676
1677 #to_napi_value
1678
1679 #from_napi_value
1680 }
1681 }
1682}
1683
1684impl TryToTokens for NapiImpl {
1685 fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
1686 self.gen_helper_mod()?.to_tokens(tokens);
1687
1688 Ok(())
1689 }
1690}
1691
1692impl NapiImpl {
1693 fn gen_helper_mod(&self) -> BindgenResult<TokenStream> {
1694 if cfg!(test) {
1695 return Ok(quote! {});
1696 }
1697
1698 let name = &self.name;
1699 let name_str = self.name.to_string();
1700 let js_name = format!("{}\0", self.js_name);
1701 let mod_name = Ident::new(
1702 &format!(
1703 "__napi_impl_helper_{}_{}",
1704 name_str,
1705 NAPI_IMPL_ID.fetch_add(1, Ordering::SeqCst)
1706 ),
1707 Span::call_site(),
1708 );
1709
1710 let register_name = &self.register_name;
1711
1712 let mut methods = vec![];
1713 let mut props = HashMap::new();
1714 let mut accessor_descriptors = HashMap::new();
1715 let mut accessor_descriptor_count = 0u32;
1716
1717 for item in self.items.iter() {
1718 let item_str = item.name.to_string();
1719 let intermediate_name = get_intermediate_ident(&item_str);
1720 methods.push(item.try_to_token_stream()?);
1721
1722 let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT;
1723 if item.writable {
1724 attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE;
1725 }
1726 if item.enumerable {
1727 attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE;
1728 }
1729 if item.configurable {
1730 attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE;
1731 }
1732 let is_static =
1733 item.fn_self.is_none() && matches!(item.kind, FnKind::Normal | FnKind::Factory);
1734 if is_static {
1735 attribute |= super::PROPERTY_ATTRIBUTE_STATIC;
1736 }
1737
1738 let property_key = (
1741 item.symbol_for.is_some(),
1742 item.well_known_symbol.is_some(),
1743 item
1744 .symbol_for
1745 .as_ref()
1746 .or(item.well_known_symbol.as_ref())
1747 .map_or(&item.js_name, |(name, _)| name),
1748 is_static,
1749 );
1750 let property = gen_member_property(&item.js_name, &item.symbol_for, &item.well_known_symbol);
1751 let prop = props.entry(property_key).or_insert_with(|| {
1752 quote! {
1753 #property.with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap())
1754 }
1755 });
1756
1757 let accessor_descriptor_ident =
1758 if matches!(item.kind, FnKind::Getter | FnKind::Setter) && !item.is_async {
1759 let entry = accessor_descriptors.entry(property_key).or_insert_with(|| {
1760 let ident = Ident::new(
1761 &format!("__napi_accessor_descriptor_{accessor_descriptor_count}"),
1762 Span::call_site(),
1763 );
1764 accessor_descriptor_count += 1;
1765 (ident, None, None)
1766 });
1767 match item.kind {
1768 FnKind::Getter => {
1769 entry.1 = Some(intermediate_name.clone());
1770 }
1771 FnKind::Setter => {
1772 entry.2 = Some(intermediate_name.clone());
1773 }
1774 _ => {}
1775 }
1776 Some(entry.0.clone())
1777 } else {
1778 None
1779 };
1780
1781 let appendix = match item.kind {
1782 FnKind::Constructor => quote! { .with_ctor(#intermediate_name) },
1783 FnKind::Getter if accessor_descriptor_ident.is_some() => {
1784 let accessor_descriptor_ident = accessor_descriptor_ident.as_ref().unwrap();
1785 quote! {
1786 .with_getter(napi::bindgen_prelude::class_getter_trampoline)
1787 .with_data(&#accessor_descriptor_ident as *const _ as *mut _)
1788 }
1789 }
1790 FnKind::Setter if accessor_descriptor_ident.is_some() => {
1791 let accessor_descriptor_ident = accessor_descriptor_ident.as_ref().unwrap();
1792 quote! {
1793 .with_setter(napi::bindgen_prelude::class_setter_trampoline)
1794 .with_data(&#accessor_descriptor_ident as *const _ as *mut _)
1795 }
1796 }
1797 FnKind::Getter => quote! { .with_getter(#intermediate_name) },
1798 FnKind::Setter => quote! { .with_setter(#intermediate_name) },
1799 _ => quote! { .with_method(#intermediate_name) },
1801 };
1802
1803 appendix.to_tokens(prop);
1804 }
1805
1806 let mut props: Vec<_> = props.into_iter().collect();
1807 props.sort_by_key(|(_, prop)| prop.to_string());
1808 let props = props.into_iter().map(|(_, prop)| prop);
1809 let props_wasm = props.clone();
1810 let mut accessor_descriptors: Vec<_> = accessor_descriptors.into_values().collect();
1811 accessor_descriptors.sort_by_key(|(ident, _, _)| ident.to_string());
1812 let accessor_descriptors = accessor_descriptors
1813 .into_iter()
1814 .map(|(ident, getter, setter)| {
1815 let getter = getter
1816 .map(|getter| quote! { Some(#getter) })
1817 .unwrap_or_else(|| quote! { None });
1818 let setter = setter
1819 .map(|setter| quote! { Some(#setter) })
1820 .unwrap_or_else(|| quote! { None });
1821 quote! {
1822 static #ident: napi::bindgen_prelude::ClassAccessorDescriptor = napi::bindgen_prelude::ClassAccessorDescriptor {
1823 getter: #getter,
1824 setter: #setter,
1825 };
1826 }
1827 });
1828 let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
1829 Ok(quote! {
1830 #[allow(non_snake_case)]
1831 #[allow(clippy::all)]
1832 mod #mod_name {
1833 use super::*;
1834 #(#accessor_descriptors)*
1835 #(#methods)*
1836
1837 #[cfg(all(not(test), not(target_family = "wasm")))]
1838 napi::ctor::declarative::ctor! {
1839 #[ctor(unsafe)]
1840 fn #register_name() {
1841 napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*], false);
1842 }
1843 }
1844
1845 #[cfg(all(not(test), target_family = "wasm"))]
1846 #[no_mangle]
1847 extern "C" fn #register_name() {
1848 napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props_wasm),*], false);
1849 }
1850 }
1851 })
1852 }
1853}
1854
1855fn gen_member_property(
1858 js_name: &str,
1859 symbol_for: &Option<(String, Span)>,
1860 well_known_symbol: &Option<(String, Span)>,
1861) -> TokenStream {
1862 match (symbol_for, well_known_symbol) {
1863 (Some((key, span)), _) => {
1865 quote_spanned! { *span=> napi::symbol_for_property!(#key) }
1866 }
1867 (None, Some((name, span))) => {
1868 quote_spanned! { *span=> napi::bindgen_prelude::Property::new().with_well_known_symbol(#name) }
1869 }
1870 (None, None) => {
1871 quote! { napi::bindgen_prelude::Property::new().with_utf8_name(#js_name).unwrap() }
1872 }
1873 }
1874}
1875
1876pub fn rm_raw_prefix(s: &str) -> &str {
1877 if let Some(stripped) = s.strip_prefix("r#") {
1878 stripped
1879 } else {
1880 s
1881 }
1882}
1883
1884fn remove_lifetime_in_type(ty: &mut syn::Type) {
1885 if let syn::Type::Path(syn::TypePath { path, .. }) = ty {
1886 path.segments.iter_mut().for_each(|segment| {
1887 if let syn::PathArguments::AngleBracketed(ref mut args) = segment.arguments {
1888 args.args.iter_mut().for_each(|arg| match arg {
1889 syn::GenericArgument::Type(ref mut ty) => {
1890 remove_lifetime_in_type(ty);
1891 }
1892 syn::GenericArgument::Lifetime(lifetime) => {
1893 lifetime.ident = Ident::new("_", lifetime.ident.span());
1894 }
1895 _ => {}
1896 });
1897 }
1898 });
1899 }
1900}