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 wrapped_value = Box::into_raw(Box::new(self));
527 let instance_value = napi::bindgen_prelude::new_instance::<#name>(env.raw(), wrapped_value as *mut _ as *mut std::ffi::c_void, ctor_ref)?;
528 Ok(napi::bindgen_prelude::ClassInstance::new(instance_value, env.raw(), wrapped_value))
529 }
530 } else {
531 Err(napi::bindgen_prelude::Error::new(
532 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
533 )
534 }
535 }
536
537 fn into_reference(self, env: napi::Env) -> napi::Result<napi::bindgen_prelude::Reference<Self>> {
538 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
539 unsafe {
540 let mut wrapped_value = Box::into_raw(Box::new(self));
541 if wrapped_value as usize == 0x1 {
542 wrapped_value = Box::into_raw(Box::new(0u8)).cast();
543 }
544 let instance_value = napi::bindgen_prelude::new_instance::<#name>(env.raw(), wrapped_value.cast(), ctor_ref)?;
545 {
546 let env = env.raw();
547 #iterator_implementation
548 #async_iterator_implementation
549 }
550 napi::bindgen_prelude::Reference::<#name>::from_value_ptr(wrapped_value.cast(), env.raw())
551 }
552 } else {
553 Err(napi::bindgen_prelude::Error::new(
554 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
555 )
556 }
557 }
558
559 fn instance_of<'env, V: napi::JsValue<'env>>(env: &napi::bindgen_prelude::Env, value: &V) -> napi::bindgen_prelude::Result<bool> {
560 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
561 let mut ctor = std::ptr::null_mut();
562 napi::check_status!(
563 unsafe { napi::sys::napi_get_reference_value(env.raw(), ctor_ref, &mut ctor) },
564 "Failed to get constructor reference of class `{}`",
565 #js_name_str
566 )?;
567 let mut is_instance_of = false;
568 napi::check_status!(
569 unsafe { napi::sys::napi_instanceof(env.raw(), value.value().value, ctor, &mut is_instance_of) },
570 "Failed to run instanceof for class `{}`",
571 #js_name_str
572 )?;
573 Ok(is_instance_of)
574 } else {
575 Err(napi::Error::new(napi::Status::GenericFailure, format!("Failed to get constructor of class `{}`", #js_name_str)))
576 }
577 }
578 }
579 }
580 }
581
582 fn gen_iterator_property(&self, class: &NapiClass, name: &Ident) -> TokenStream {
583 if !class.implement_iterator {
584 return quote! {};
585 }
586 quote! {
587 unsafe { napi::__private::create_iterator::<#name>(env, instance_value, wrapped_value); }
588 }
589 }
590
591 fn gen_async_iterator_property(&self, class: &NapiClass, name: &Ident) -> TokenStream {
592 if !class.implement_async_iterator {
593 return quote! {};
594 }
595 quote! {
601 napi::__private::create_async_iterator::<#name>(env, instance_value, wrapped_value);
602 }
603 }
604
605 fn gen_to_napi_value_ctor_impl(&self, class: &NapiClass) -> TokenStream {
606 let name = &self.name;
607 let js_name_without_null = &self.js_name;
608 let js_name_str = format!("{}\0", self.js_name);
609
610 let mut field_conversions = vec![];
611 let mut field_destructions = vec![];
612
613 for field in class.fields.iter() {
614 let ty = &field.ty;
615
616 match &field.name {
617 syn::Member::Named(ident) => {
618 let alias_ident = format_ident!("{}_", ident);
620 field_destructions.push(quote! { #ident: #alias_ident });
621 field_conversions.push(
622 quote! { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #alias_ident)? },
623 );
624 }
625 syn::Member::Unnamed(i) => {
626 let arg_name = format_ident!("arg{}", i);
627 field_destructions.push(quote! { #arg_name });
628 field_conversions.push(
629 quote! { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #arg_name)? },
630 );
631 }
632 }
633 }
634
635 let destructed_fields = if class.is_tuple {
636 quote! {
637 Self (#(#field_destructions),*)
638 }
639 } else {
640 quote! {
641 Self {#(#field_destructions),*}
642 }
643 };
644
645 let finalize_trait = if class.use_custom_finalize {
646 quote! {}
647 } else if self.has_lifetime {
648 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ObjectFinalize for #name<'_javascript_function_scope> {} }
649 } else {
650 quote! { impl napi::bindgen_prelude::ObjectFinalize for #name {} }
651 };
652
653 let to_napi_value_impl = if self.has_lifetime {
654 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name<'_javascript_function_scope> }
655 } else {
656 quote! { impl napi::bindgen_prelude::ToNapiValue for #name }
657 };
658
659 quote! {
660 #[automatically_derived]
661 #to_napi_value_impl {
662 unsafe fn to_napi_value(
663 env: napi::bindgen_prelude::sys::napi_env,
664 val: #name,
665 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
666 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
667 let mut ctor = std::ptr::null_mut();
668
669 napi::bindgen_prelude::check_status!(
670 napi::bindgen_prelude::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
671 "Failed to get constructor reference of class `{}`",
672 #js_name_without_null
673 )?;
674
675 let mut instance_value = std::ptr::null_mut();
676 let #destructed_fields = val;
677 let args = vec![#(#field_conversions),*];
678
679 napi::bindgen_prelude::check_status!(
680 napi::bindgen_prelude::sys::napi_new_instance(env, ctor, args.len(), args.as_ptr(), &mut instance_value),
681 "Failed to construct class `{}`",
682 #js_name_without_null
683 )?;
684
685 Ok(instance_value)
686 } else {
687 Err(napi::bindgen_prelude::Error::new(
688 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_str))
689 )
690 }
691 }
692 }
693 #finalize_trait
694 }
695 }
696
697 fn gen_to_napi_value_obj_impl(&self, obj: &NapiObject) -> TokenStream {
698 let name = &self.name;
699 let name_str = self.name.to_string();
700
701 let mut obj_field_getters = vec![];
702 let mut field_destructions = vec![];
703
704 let mut value_conversions = vec![];
706 let mut property_descriptors = vec![];
707 let mut conditional_setters = vec![];
708 let mut value_names = vec![];
709
710 for (idx, field) in obj.fields.iter().enumerate() {
711 let field_js_name = &field.js_name;
712 let field_name_c_string = gen_field_name_c_string(field_js_name);
713 let mut ty = field.ty.clone();
714 remove_lifetime_in_type(&mut ty);
715 let is_optional_field = is_option_type(&ty);
716
717 let is_always_set = !is_optional_field || self.use_nullable;
719
720 match &field.name {
721 syn::Member::Named(ident) => {
722 let alias_ident = format_ident!("{}_", ident);
723 field_destructions.push(quote! { #ident: #alias_ident });
724
725 if is_always_set {
726 let value_var = Ident::new(&format!("__obj_value_{}", idx), Span::call_site());
728 value_names.push(value_var.clone());
729
730 if is_optional_field {
731 value_conversions.push(quote! {
733 let #value_var = if let Some(inner) = #alias_ident {
734 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, inner)?
735 } else {
736 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, napi::bindgen_prelude::Null)?
737 };
738 });
739 } else {
740 value_conversions.push(quote! {
742 let #value_var = napi::bindgen_prelude::ToNapiValue::to_napi_value(env, #alias_ident)?;
743 });
744 }
745
746 property_descriptors.push(gen_named_property_descriptor(
747 &field_name_c_string,
748 &value_var,
749 ));
750 } else {
751 conditional_setters.push(gen_optional_conditional_setter(
753 &alias_ident,
754 &field_name_c_string,
755 quote! { obj_ptr },
756 ));
757 }
758
759 let raw_ident = Ident::new(&format!("__obj_field_raw_{}", idx), Span::call_site());
760 obj_field_getters.push(gen_raw_field_getter(
761 &alias_ident,
762 &raw_ident,
763 &ty,
764 field_js_name,
765 &field_name_c_string,
766 &name_str,
767 is_optional_field,
768 self.use_nullable,
769 quote! { napi::bindgen_prelude::JsValue::raw(&obj) },
770 ));
771 }
772 syn::Member::Unnamed(i) => {
773 let arg_name = format_ident!("arg{}", i);
774 field_destructions.push(quote! { #arg_name });
775
776 if is_always_set {
777 let value_var = Ident::new(&format!("__obj_value_{}", idx), Span::call_site());
779 value_names.push(value_var.clone());
780
781 if is_optional_field {
782 value_conversions.push(quote! {
784 let #value_var = if let Some(inner) = #arg_name {
785 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, inner)?
786 } else {
787 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, napi::bindgen_prelude::Null)?
788 };
789 });
790 } else {
791 value_conversions.push(quote! {
793 let #value_var = napi::bindgen_prelude::ToNapiValue::to_napi_value(env, #arg_name)?;
794 });
795 }
796
797 property_descriptors.push(gen_named_property_descriptor(
798 &field_name_c_string,
799 &value_var,
800 ));
801 } else {
802 conditional_setters.push(gen_optional_conditional_setter(
804 &arg_name,
805 &field_name_c_string,
806 quote! { obj_ptr },
807 ));
808 }
809
810 let raw_ident = Ident::new(&format!("__obj_field_raw_{}", idx), Span::call_site());
811 obj_field_getters.push(gen_raw_field_getter(
812 &arg_name,
813 &raw_ident,
814 &ty,
815 field_js_name,
816 &field_name_c_string,
817 "",
818 is_optional_field,
819 self.use_nullable,
820 quote! { napi::bindgen_prelude::JsValue::raw(&obj) },
821 ));
822 }
823 }
824 }
825
826 let destructed_fields = if obj.is_tuple {
827 quote! {
828 Self (#(#field_destructions),*)
829 }
830 } else {
831 quote! {
832 Self {#(#field_destructions),*}
833 }
834 };
835
836 let name_with_lifetime = if self.has_lifetime {
837 quote! { #name<'_javascript_function_scope> }
838 } else {
839 quote! { #name }
840 };
841 let (from_napi_value_impl, to_napi_value_impl, validate_napi_value_impl, type_name_impl) =
842 if self.has_lifetime {
843 (
844 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::FromNapiValue for #name<'_javascript_function_scope> },
845 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name<'_javascript_function_scope> },
846 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ValidateNapiValue for #name<'_javascript_function_scope> },
847 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::TypeName for #name<'_javascript_function_scope> },
848 )
849 } else {
850 (
851 quote! { impl napi::bindgen_prelude::FromNapiValue for #name },
852 quote! { impl napi::bindgen_prelude::ToNapiValue for #name },
853 quote! { impl napi::bindgen_prelude::ValidateNapiValue for #name },
854 quote! { impl napi::bindgen_prelude::TypeName for #name },
855 )
856 };
857
858 let object_creation = if conditional_setters.is_empty() {
860 quote! {
862 #(#value_conversions)*
864
865 let properties = [
866 #(#property_descriptors),*
867 ];
868
869 let obj_ptr = napi::bindgen_prelude::create_object_with_properties(env, &properties)?;
870 Ok(obj_ptr)
871 }
872 } else {
873 quote! {
875 #(#value_conversions)*
877
878 let properties = [
879 #(#property_descriptors),*
880 ];
881
882 let obj_ptr = napi::bindgen_prelude::create_object_with_properties(env, &properties)?;
883
884 #(#conditional_setters)*
885
886 Ok(obj_ptr)
887 }
888 };
889
890 let to_napi_value = if obj.object_to_js {
891 quote! {
892 #[automatically_derived]
893 #to_napi_value_impl {
894 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> {
895 let #destructed_fields = val;
896 #object_creation
897 }
898 }
899 }
900 } else {
901 quote! {}
902 };
903
904 let from_napi_value = if obj.object_from_js {
905 let return_type = if self.has_lifetime {
906 quote! { #name<'_javascript_function_scope> }
907 } else {
908 quote! { #name }
909 };
910 quote! {
911 #[automatically_derived]
912 #from_napi_value_impl {
913 unsafe fn from_napi_value(
914 env: napi::bindgen_prelude::sys::napi_env,
915 napi_val: napi::bindgen_prelude::sys::napi_value
916 ) -> napi::bindgen_prelude::Result<#return_type> {
917 #[allow(unused_variables)]
918 let env_wrapper = napi::bindgen_prelude::Env::from(env);
919 #[allow(unused_mut)]
920 let mut obj = napi::bindgen_prelude::Object::from_napi_value(env, napi_val)?;
921
922 #(#obj_field_getters)*
923
924 let val = #destructed_fields;
925
926 Ok(val)
927 }
928 }
929
930 #[automatically_derived]
931 #validate_napi_value_impl {}
932 }
933 } else {
934 quote! {}
935 };
936
937 quote! {
938 #[automatically_derived]
939 #type_name_impl {
940 fn type_name() -> &'static str {
941 #name_str
942 }
943
944 fn value_type() -> napi::ValueType {
945 napi::ValueType::Object
946 }
947 }
948
949 #to_napi_value
950
951 #from_napi_value
952 }
953 }
954
955 fn gen_default_getters_setters(&self, class: &NapiClass) -> Vec<(String, TokenStream)> {
956 let mut getters_setters = vec![];
957 let struct_name = &self.name;
958 let js_name_str = &self.js_name;
959
960 for field in class.fields.iter() {
961 let field_ident = &field.name;
962 let field_name = match &field.name {
963 syn::Member::Named(ident) => ident.to_string(),
964 syn::Member::Unnamed(i) => format!("field{}", i.index),
965 };
966 let ty = &field.ty;
967
968 let getter_name = Ident::new(
969 &format!("get_{}", rm_raw_prefix(&field_name)),
970 Span::call_site(),
971 );
972 let setter_name = Ident::new(
973 &format!("set_{}", rm_raw_prefix(&field_name)),
974 Span::call_site(),
975 );
976 let accessor_descriptor_name = Ident::new(
977 &format!(
978 "__napi_field_accessor_descriptor_{}",
979 rm_raw_prefix(&field_name)
980 ),
981 Span::call_site(),
982 );
983
984 if field.getter {
985 let default_to_napi_value_convert = quote! {
986 let val = &mut obj.#field_ident;
987 unsafe { <&mut #ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) }
988 };
989 let to_napi_value_convert = if let syn::Type::Path(syn::TypePath {
990 path: syn::Path { segments, .. },
991 ..
992 }) = ty
993 {
994 if let Some(syn::PathSegment { ident, .. }) = segments.last() {
995 if STRUCT_FIELD_SPECIAL_CASE.iter().any(|name| ident == name) {
996 quote! {
997 let val = obj.#field_ident.as_mut();
998 unsafe { napi::bindgen_prelude::ToNapiValue::to_napi_value(env, val) }
999 }
1000 } else {
1001 default_to_napi_value_convert
1002 }
1003 } else {
1004 default_to_napi_value_convert
1005 }
1006 } else {
1007 default_to_napi_value_convert
1008 };
1009 let tracing_debug = gen_tracing_debug(js_name_str, &field.js_name);
1010 getters_setters.push((
1011 field.js_name.clone(),
1012 quote! {
1013 unsafe fn #getter_name(
1014 env: napi::bindgen_prelude::sys::napi_env,
1015 this: napi::bindgen_prelude::sys::napi_value
1016 ) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
1017 #tracing_debug
1018 let this_ptr = unsafe {
1019 napi::bindgen_prelude::class_accessor_unwrap_this::<#struct_name>(env, this)?
1020 };
1021 let _napi_native_borrow =
1024 napi::bindgen_prelude::acquire_native_borrow(this_ptr, true)?;
1025 let obj: &mut #struct_name = Box::leak(unsafe { Box::from_raw(this_ptr) });
1026 #to_napi_value_convert
1027 }
1028 },
1029 ));
1030 }
1031
1032 if field.setter {
1033 let setter_tracing_debug =
1034 gen_tracing_debug(js_name_str, &format!("set_{}", field.js_name));
1035 getters_setters.push((
1036 field.js_name.clone(),
1037 quote! {
1038 unsafe fn #setter_name(
1039 env: napi::bindgen_prelude::sys::napi_env,
1040 this: napi::bindgen_prelude::sys::napi_value,
1041 value: napi::bindgen_prelude::sys::napi_value
1042 ) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
1043 #setter_tracing_debug
1044 let val = unsafe {
1045 <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, value)?
1046 };
1047 let this_ptr = unsafe {
1048 napi::bindgen_prelude::class_accessor_unwrap_this::<#struct_name>(env, this)?
1049 };
1050 let _napi_native_borrow =
1052 napi::bindgen_prelude::acquire_native_borrow(this_ptr, true)?;
1053 let obj: &mut #struct_name = Box::leak(unsafe { Box::from_raw(this_ptr) });
1054 obj.#field_ident = val;
1055 unsafe { <() as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, ()) }
1056 }
1057 },
1058 ));
1059 }
1060
1061 if field.getter {
1062 let getter = quote! { Some(#getter_name) };
1063 let setter = if field.setter {
1064 quote! { Some(#setter_name) }
1065 } else {
1066 quote! { None }
1067 };
1068 getters_setters.push((
1069 field.js_name.clone(),
1070 quote! {
1071 static #accessor_descriptor_name: napi::bindgen_prelude::ClassAccessorDescriptor = napi::bindgen_prelude::ClassAccessorDescriptor {
1072 getter: #getter,
1073 setter: #setter,
1074 };
1075 },
1076 ));
1077 }
1078 }
1079
1080 getters_setters
1081 }
1082
1083 fn gen_register(&self, class: &NapiClass) -> TokenStream {
1084 let name = &self.name;
1085 let struct_register_name = &self.register_name;
1086 let js_name = format!("{}\0", self.js_name);
1087 let implement_iterator = class.implement_iterator;
1088 let mut props = vec![];
1089
1090 if class.ctor {
1091 props.push(quote! { napi::bindgen_prelude::Property::new().with_utf8_name("constructor").unwrap().with_ctor(constructor) });
1092 }
1093
1094 for field in class.fields.iter() {
1095 let field_name = match &field.name {
1096 syn::Member::Named(ident) => ident.to_string(),
1097 syn::Member::Unnamed(i) => format!("field{}", i.index),
1098 };
1099
1100 if !field.getter {
1101 continue;
1102 }
1103
1104 let js_name = &field.js_name;
1105 let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT;
1106 if field.writable {
1107 attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE;
1108 }
1109 if field.enumerable {
1110 attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE;
1111 }
1112 if field.configurable {
1113 attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE;
1114 }
1115
1116 let mut prop = quote! {
1117 napi::bindgen_prelude::Property::new().with_utf8_name(#js_name)
1118 .unwrap()
1119 .with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap())
1120 };
1121
1122 if field.getter {
1123 let accessor_descriptor_name = Ident::new(
1124 &format!(
1125 "__napi_field_accessor_descriptor_{}",
1126 rm_raw_prefix(&field_name)
1127 ),
1128 Span::call_site(),
1129 );
1130 (quote! {
1131 .with_getter(napi::bindgen_prelude::class_getter_trampoline)
1132 .with_data(&#accessor_descriptor_name as *const _ as *mut _)
1133 })
1134 .to_tokens(&mut prop);
1135 }
1136
1137 if field.writable && field.setter {
1138 let accessor_descriptor_name = Ident::new(
1139 &format!(
1140 "__napi_field_accessor_descriptor_{}",
1141 rm_raw_prefix(&field_name)
1142 ),
1143 Span::call_site(),
1144 );
1145 (quote! {
1146 .with_setter(napi::bindgen_prelude::class_setter_trampoline)
1147 .with_data(&#accessor_descriptor_name as *const _ as *mut _)
1148 })
1149 .to_tokens(&mut prop);
1150 }
1151
1152 props.push(prop);
1153 }
1154 let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
1155 quote! {
1156 #[cfg(all(not(test), not(target_family = "wasm")))]
1157 napi::ctor::declarative::ctor! {
1158 #[allow(non_snake_case)]
1159 #[allow(clippy::all)]
1160 #[ctor(unsafe)]
1161 fn #struct_register_name() {
1162 napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*], #implement_iterator);
1163 }
1164 }
1165
1166 #[allow(non_snake_case)]
1167 #[allow(clippy::all)]
1168 #[cfg(all(not(test), target_family = "wasm"))]
1169 #[no_mangle]
1170 extern "C" fn #struct_register_name() {
1171 napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*], #implement_iterator);
1172 }
1173 }
1174 }
1175
1176 fn gen_to_napi_value_structured_enum_impl(
1177 &self,
1178 structured_enum: &NapiStructuredEnum,
1179 ) -> TokenStream {
1180 let name = &self.name;
1181 let name_str = self.name.to_string();
1182 let discriminant = structured_enum.discriminant.as_str();
1183 let discriminant_c_string = gen_field_name_c_string(discriminant);
1184
1185 let mut variant_arm_setters = vec![];
1186 let mut variant_arm_getters = vec![];
1187
1188 for variant in structured_enum.variants.iter() {
1189 let variant_name = &variant.name;
1190 let mut variant_name_str = variant_name.to_string();
1191 if let Some(case) = structured_enum.discriminant_case {
1192 variant_name_str = to_case(variant_name_str, case);
1193 }
1194
1195 let mut obj_field_getters = vec![];
1196 let mut field_destructions = vec![];
1197
1198 let mut value_conversions = vec![];
1200 let mut property_descriptors = vec![];
1201 let mut conditional_setters = vec![];
1202
1203 let discriminant_value_var = Ident::new("__discriminant_value", Span::call_site());
1205 value_conversions.push(quote! {
1206 let #discriminant_value_var = napi::bindgen_prelude::ToNapiValue::to_napi_value(env, #variant_name_str)?;
1207 });
1208 property_descriptors.push(gen_named_property_descriptor(
1209 &discriminant_c_string,
1210 &discriminant_value_var,
1211 ));
1212
1213 for (idx, field) in variant.fields.iter().enumerate() {
1214 let field_js_name = &field.js_name;
1215 let field_name_c_string = gen_field_name_c_string(field_js_name);
1216 let mut ty = field.ty.clone();
1217 remove_lifetime_in_type(&mut ty);
1218 let is_optional_field = is_option_type(&ty);
1219
1220 let is_always_set = !is_optional_field || self.use_nullable;
1222
1223 match &field.name {
1224 syn::Member::Named(ident) => {
1225 let alias_ident = format_ident!("{}_", ident);
1226 field_destructions.push(quote! { #ident: #alias_ident });
1227
1228 if is_always_set {
1229 let value_var = Ident::new(&format!("__variant_value_{}", idx), Span::call_site());
1231
1232 if is_optional_field {
1233 value_conversions.push(quote! {
1235 let #value_var = if let Some(inner) = #alias_ident {
1236 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, inner)?
1237 } else {
1238 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, napi::bindgen_prelude::Null)?
1239 };
1240 });
1241 } else {
1242 value_conversions.push(quote! {
1244 let #value_var = napi::bindgen_prelude::ToNapiValue::to_napi_value(env, #alias_ident)?;
1245 });
1246 }
1247
1248 property_descriptors.push(gen_named_property_descriptor(
1249 &field_name_c_string,
1250 &value_var,
1251 ));
1252 } else {
1253 conditional_setters.push(gen_optional_conditional_setter(
1255 &alias_ident,
1256 &field_name_c_string,
1257 quote! { obj_ptr },
1258 ));
1259 }
1260
1261 let raw_ident = Ident::new(&format!("__variant_field_raw_{}", idx), Span::call_site());
1262 obj_field_getters.push(gen_raw_field_getter(
1263 &alias_ident,
1264 &raw_ident,
1265 &ty,
1266 field_js_name,
1267 &field_name_c_string,
1268 &name_str,
1269 is_optional_field,
1270 self.use_nullable,
1271 quote! { napi::bindgen_prelude::JsValue::raw(&obj) },
1272 ));
1273 }
1274 syn::Member::Unnamed(i) => {
1275 let arg_name = format_ident!("arg{}", i);
1276 field_destructions.push(quote! { #arg_name });
1277
1278 if is_always_set {
1279 let value_var = Ident::new(&format!("__variant_value_{}", idx), Span::call_site());
1281
1282 if is_optional_field {
1283 value_conversions.push(quote! {
1285 let #value_var = if let Some(inner) = #arg_name {
1286 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, inner)?
1287 } else {
1288 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, napi::bindgen_prelude::Null)?
1289 };
1290 });
1291 } else {
1292 value_conversions.push(quote! {
1294 let #value_var = napi::bindgen_prelude::ToNapiValue::to_napi_value(env, #arg_name)?;
1295 });
1296 }
1297
1298 property_descriptors.push(gen_named_property_descriptor(
1299 &field_name_c_string,
1300 &value_var,
1301 ));
1302 } else {
1303 conditional_setters.push(gen_optional_conditional_setter(
1305 &arg_name,
1306 &field_name_c_string,
1307 quote! { obj_ptr },
1308 ));
1309 }
1310
1311 let raw_ident = Ident::new(&format!("__variant_field_raw_{}", idx), Span::call_site());
1312 obj_field_getters.push(gen_raw_field_getter(
1313 &arg_name,
1314 &raw_ident,
1315 &ty,
1316 field_js_name,
1317 &field_name_c_string,
1318 "",
1319 is_optional_field,
1320 self.use_nullable,
1321 quote! { napi::bindgen_prelude::JsValue::raw(&obj) },
1322 ));
1323 }
1324 }
1325 }
1326
1327 let destructed_fields = if variant.is_tuple {
1328 quote! {
1329 Self::#variant_name (#(#field_destructions),*)
1330 }
1331 } else {
1332 quote! {
1333 Self::#variant_name {#(#field_destructions),*}
1334 }
1335 };
1336
1337 let variant_object_creation = if conditional_setters.is_empty() {
1339 quote! {
1341 #(#value_conversions)*
1342
1343 let properties = [
1344 #(#property_descriptors),*
1345 ];
1346
1347 napi::bindgen_prelude::create_object_with_properties(env, &properties)
1348 }
1349 } else {
1350 quote! {
1352 #(#value_conversions)*
1353
1354 let properties = [
1355 #(#property_descriptors),*
1356 ];
1357
1358 let obj_ptr = napi::bindgen_prelude::create_object_with_properties(env, &properties)?;
1359
1360 #(#conditional_setters)*
1361
1362 Ok(obj_ptr)
1363 }
1364 };
1365
1366 variant_arm_setters.push(quote! {
1367 #destructed_fields => {
1368 #variant_object_creation
1369 },
1370 });
1371
1372 variant_arm_getters.push(quote! {
1373 #variant_name_str => {
1374 #(#obj_field_getters)*
1375 #destructed_fields
1376 },
1377 })
1378 }
1379
1380 let to_napi_value = if structured_enum.object_to_js {
1381 quote! {
1382 impl napi::bindgen_prelude::ToNapiValue for #name {
1383 unsafe fn to_napi_value(env: napi::bindgen_prelude::sys::napi_env, val: #name) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
1384 match val {
1385 #(#variant_arm_setters)*
1386 }
1387 }
1388 }
1389 }
1390 } else {
1391 quote! {}
1392 };
1393
1394 let from_napi_value = if structured_enum.object_from_js {
1395 quote! {
1396 impl napi::bindgen_prelude::FromNapiValue for #name {
1397 unsafe fn from_napi_value(
1398 env: napi::bindgen_prelude::sys::napi_env,
1399 napi_val: napi::bindgen_prelude::sys::napi_value
1400 ) -> napi::bindgen_prelude::Result<Self> {
1401 #[allow(unused_variables)]
1402 let env_wrapper = napi::bindgen_prelude::Env::from(env);
1403 #[allow(unused_mut)]
1404 let mut obj = napi::bindgen_prelude::Object::from_napi_value(env, napi_val)?;
1405 let __discriminant_raw = napi::bindgen_prelude::get_named_property_raw(
1406 env,
1407 napi::bindgen_prelude::JsValue::raw(&obj),
1408 #discriminant_c_string,
1409 )?;
1410 let type_: String = napi::bindgen_prelude::from_raw_required_field(
1411 env,
1412 __discriminant_raw,
1413 #name_str,
1414 #discriminant,
1415 )?;
1416 let val = match type_.as_str() {
1417 #(#variant_arm_getters)*
1418 _ => return Err(napi::bindgen_prelude::Error::new(
1419 napi::bindgen_prelude::Status::InvalidArg,
1420 format!("Unknown variant `{}`", type_),
1421 )),
1422 };
1423
1424 Ok(val)
1425 }
1426 }
1427
1428 impl napi::bindgen_prelude::ValidateNapiValue for #name {}
1429 }
1430 } else {
1431 quote! {}
1432 };
1433
1434 quote! {
1435 impl napi::bindgen_prelude::TypeName for #name {
1436 fn type_name() -> &'static str {
1437 #name_str
1438 }
1439
1440 fn value_type() -> napi::ValueType {
1441 napi::ValueType::Object
1442 }
1443 }
1444
1445 #to_napi_value
1446
1447 #from_napi_value
1448 }
1449 }
1450
1451 fn gen_napi_value_transparent_impl(&self, transparent: &NapiTransparent) -> TokenStream {
1452 let name = &self.name;
1453 let name = if self.has_lifetime {
1454 quote! { #name<'_> }
1455 } else {
1456 quote! { #name }
1457 };
1458 let inner_type = transparent.ty.clone().into_token_stream();
1459
1460 let to_napi_value = if transparent.object_to_js {
1461 quote! {
1462 #[automatically_derived]
1463 impl napi::bindgen_prelude::ToNapiValue for #name {
1464 unsafe fn to_napi_value(
1465 env: napi::bindgen_prelude::sys::napi_env,
1466 val: Self
1467 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
1468 <#inner_type>::to_napi_value(env, val.0)
1469 }
1470 }
1471 }
1472 } else {
1473 quote! {}
1474 };
1475
1476 let from_napi_value = if transparent.object_from_js {
1477 quote! {
1478 #[automatically_derived]
1479 impl napi::bindgen_prelude::FromNapiValue for #name {
1480 unsafe fn from_napi_value(
1481 env: napi::bindgen_prelude::sys::napi_env,
1482 napi_val: napi::bindgen_prelude::sys::napi_value
1483 ) -> napi::bindgen_prelude::Result<Self> {
1484 Ok(Self(<#inner_type>::from_napi_value(env, napi_val)?))
1485 }
1486 }
1487 }
1488 } else {
1489 quote! {}
1490 };
1491
1492 quote! {
1493 #[automatically_derived]
1494 impl napi::bindgen_prelude::TypeName for #name {
1495 fn type_name() -> &'static str {
1496 <#inner_type>::type_name()
1497 }
1498
1499 fn value_type() -> napi::ValueType {
1500 <#inner_type>::value_type()
1501 }
1502 }
1503
1504 #[automatically_derived]
1505 impl napi::bindgen_prelude::ValidateNapiValue for #name {
1506 unsafe fn validate(
1507 env: napi::bindgen_prelude::sys::napi_env,
1508 napi_val: napi::bindgen_prelude::sys::napi_value
1509 ) -> napi::bindgen_prelude::Result<napi::sys::napi_value> {
1510 <#inner_type>::validate(env, napi_val)
1511 }
1512 }
1513
1514 #to_napi_value
1515
1516 #from_napi_value
1517 }
1518 }
1519
1520 fn gen_napi_value_array_impl(&self, array: &NapiArray) -> TokenStream {
1521 let name = &self.name;
1522 let name_str = self.name.to_string();
1523
1524 let mut obj_field_setters = vec![];
1525 let mut obj_field_getters = vec![];
1526 let mut field_destructions = vec![];
1527
1528 for field in array.fields.iter() {
1529 let mut ty = field.ty.clone();
1530 remove_lifetime_in_type(&mut ty);
1531 let is_optional_field = if let syn::Type::Path(syn::TypePath {
1532 path: syn::Path { segments, .. },
1533 ..
1534 }) = &ty
1535 {
1536 if let Some(last_path) = segments.last() {
1537 last_path.ident == "Option"
1538 } else {
1539 false
1540 }
1541 } else {
1542 false
1543 };
1544
1545 if let syn::Member::Unnamed(i) = &field.name {
1546 let arg_name = format_ident!("arg{}", i);
1547 let field_index = i.index;
1548 field_destructions.push(quote! { #arg_name });
1549 if is_optional_field {
1550 obj_field_setters.push(match self.use_nullable {
1551 false => quote! {
1552 if #arg_name.is_some() {
1553 array.set(#field_index, #arg_name)?;
1554 }
1555 },
1556 true => quote! {
1557 if let Some(#arg_name) = #arg_name {
1558 array.set(#field_index, #arg_name)?;
1559 } else {
1560 array.set(#field_index, napi::bindgen_prelude::Null)?;
1561 }
1562 },
1563 });
1564 } else {
1565 obj_field_setters.push(quote! { array.set(#field_index, #arg_name)?; });
1566 }
1567 if is_optional_field && !self.use_nullable {
1568 obj_field_getters.push(quote! { let #arg_name: #ty = array.get(#field_index)?; });
1569 } else {
1570 obj_field_getters.push(quote! {
1571 let #arg_name: #ty = array.get(#field_index)?.ok_or_else(|| napi::bindgen_prelude::Error::new(
1572 napi::bindgen_prelude::Status::InvalidArg,
1573 format!("Failed to get element with index `{}`", #field_index),
1574 ))?;
1575 });
1576 }
1577 }
1578 }
1579
1580 let destructed_fields = quote! {
1581 Self (#(#field_destructions),*)
1582 };
1583
1584 let name_with_lifetime = if self.has_lifetime {
1585 quote! { #name<'_javascript_function_scope> }
1586 } else {
1587 quote! { #name }
1588 };
1589 let (from_napi_value_impl, to_napi_value_impl, validate_napi_value_impl, type_name_impl) =
1590 if self.has_lifetime {
1591 (
1592 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::FromNapiValue for #name<'_javascript_function_scope> },
1593 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name<'_javascript_function_scope> },
1594 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ValidateNapiValue for #name<'_javascript_function_scope> },
1595 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::TypeName for #name<'_javascript_function_scope> },
1596 )
1597 } else {
1598 (
1599 quote! { impl napi::bindgen_prelude::FromNapiValue for #name },
1600 quote! { impl napi::bindgen_prelude::ToNapiValue for #name },
1601 quote! { impl napi::bindgen_prelude::ValidateNapiValue for #name },
1602 quote! { impl napi::bindgen_prelude::TypeName for #name },
1603 )
1604 };
1605
1606 let array_len = array.fields.len() as u32;
1607
1608 let to_napi_value = if array.object_to_js {
1609 quote! {
1610 #[automatically_derived]
1611 #to_napi_value_impl {
1612 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> {
1613 #[allow(unused_variables)]
1614 let env_wrapper = napi::bindgen_prelude::Env::from(env);
1615 #[allow(unused_mut)]
1616 let mut array = env_wrapper.create_array(#array_len)?;
1617
1618 let #destructed_fields = val;
1619 #(#obj_field_setters)*
1620
1621 napi::bindgen_prelude::Array::to_napi_value(env, array)
1622 }
1623 }
1624 }
1625 } else {
1626 quote! {}
1627 };
1628
1629 let from_napi_value = if array.object_from_js {
1630 let return_type = if self.has_lifetime {
1631 quote! { #name<'_javascript_function_scope> }
1632 } else {
1633 quote! { #name }
1634 };
1635 quote! {
1636 #[automatically_derived]
1637 #from_napi_value_impl {
1638 unsafe fn from_napi_value(
1639 env: napi::bindgen_prelude::sys::napi_env,
1640 napi_val: napi::bindgen_prelude::sys::napi_value
1641 ) -> napi::bindgen_prelude::Result<#return_type> {
1642 #[allow(unused_variables)]
1643 let env_wrapper = napi::bindgen_prelude::Env::from(env);
1644 #[allow(unused_mut)]
1645 let mut array = napi::bindgen_prelude::Array::from_napi_value(env, napi_val)?;
1646
1647 #(#obj_field_getters)*
1648
1649 let val = #destructed_fields;
1650
1651 Ok(val)
1652 }
1653 }
1654
1655 #[automatically_derived]
1656 #validate_napi_value_impl {}
1657 }
1658 } else {
1659 quote! {}
1660 };
1661
1662 quote! {
1663 #[automatically_derived]
1664 #type_name_impl {
1665 fn type_name() -> &'static str {
1666 #name_str
1667 }
1668
1669 fn value_type() -> napi::ValueType {
1670 napi::ValueType::Object
1671 }
1672 }
1673
1674 #to_napi_value
1675
1676 #from_napi_value
1677 }
1678 }
1679}
1680
1681impl TryToTokens for NapiImpl {
1682 fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
1683 self.gen_helper_mod()?.to_tokens(tokens);
1684
1685 Ok(())
1686 }
1687}
1688
1689impl NapiImpl {
1690 fn gen_helper_mod(&self) -> BindgenResult<TokenStream> {
1691 if cfg!(test) {
1692 return Ok(quote! {});
1693 }
1694
1695 let name = &self.name;
1696 let name_str = self.name.to_string();
1697 let js_name = format!("{}\0", self.js_name);
1698 let mod_name = Ident::new(
1699 &format!(
1700 "__napi_impl_helper_{}_{}",
1701 name_str,
1702 NAPI_IMPL_ID.fetch_add(1, Ordering::SeqCst)
1703 ),
1704 Span::call_site(),
1705 );
1706
1707 let register_name = &self.register_name;
1708
1709 let mut methods = vec![];
1710 let mut props = HashMap::new();
1711 let mut accessor_descriptors = HashMap::new();
1712 let mut accessor_descriptor_count = 0u32;
1713
1714 for item in self.items.iter() {
1715 let js_name = Literal::string(&item.js_name);
1716 let item_str = item.name.to_string();
1717 let intermediate_name = get_intermediate_ident(&item_str);
1718 methods.push(item.try_to_token_stream()?);
1719
1720 let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT;
1721 if item.writable {
1722 attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE;
1723 }
1724 if item.enumerable {
1725 attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE;
1726 }
1727 if item.configurable {
1728 attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE;
1729 }
1730
1731 let prop = props.entry(&item.js_name).or_insert_with(|| {
1732 quote! {
1733 napi::bindgen_prelude::Property::new().with_utf8_name(#js_name).unwrap().with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap())
1734 }
1735 });
1736
1737 let accessor_descriptor_ident =
1738 if matches!(item.kind, FnKind::Getter | FnKind::Setter) && !item.is_async {
1739 let entry = accessor_descriptors
1740 .entry(item.js_name.clone())
1741 .or_insert_with(|| {
1742 let ident = Ident::new(
1743 &format!("__napi_accessor_descriptor_{accessor_descriptor_count}"),
1744 Span::call_site(),
1745 );
1746 accessor_descriptor_count += 1;
1747 (ident, None, None)
1748 });
1749 match item.kind {
1750 FnKind::Getter => {
1751 entry.1 = Some(intermediate_name.clone());
1752 }
1753 FnKind::Setter => {
1754 entry.2 = Some(intermediate_name.clone());
1755 }
1756 _ => {}
1757 }
1758 Some(entry.0.clone())
1759 } else {
1760 None
1761 };
1762
1763 let appendix = match item.kind {
1764 FnKind::Constructor => quote! { .with_ctor(#intermediate_name) },
1765 FnKind::Getter if accessor_descriptor_ident.is_some() => {
1766 let accessor_descriptor_ident = accessor_descriptor_ident.as_ref().unwrap();
1767 quote! {
1768 .with_getter(napi::bindgen_prelude::class_getter_trampoline)
1769 .with_data(&#accessor_descriptor_ident as *const _ as *mut _)
1770 }
1771 }
1772 FnKind::Setter if accessor_descriptor_ident.is_some() => {
1773 let accessor_descriptor_ident = accessor_descriptor_ident.as_ref().unwrap();
1774 quote! {
1775 .with_setter(napi::bindgen_prelude::class_setter_trampoline)
1776 .with_data(&#accessor_descriptor_ident as *const _ as *mut _)
1777 }
1778 }
1779 FnKind::Getter => quote! { .with_getter(#intermediate_name) },
1780 FnKind::Setter => quote! { .with_setter(#intermediate_name) },
1781 _ => {
1782 if item.fn_self.is_some() {
1783 quote! { .with_method(#intermediate_name) }
1784 } else {
1785 quote! { .with_method(#intermediate_name).with_property_attributes(napi::bindgen_prelude::PropertyAttributes::Static) }
1786 }
1787 }
1788 };
1789
1790 appendix.to_tokens(prop);
1791 }
1792
1793 let mut props: Vec<_> = props.into_iter().collect();
1794 props.sort_by_key(|(_, prop)| prop.to_string());
1795 let props = props.into_iter().map(|(_, prop)| prop);
1796 let props_wasm = props.clone();
1797 let mut accessor_descriptors: Vec<_> = accessor_descriptors.into_values().collect();
1798 accessor_descriptors.sort_by_key(|(ident, _, _)| ident.to_string());
1799 let accessor_descriptors = accessor_descriptors
1800 .into_iter()
1801 .map(|(ident, getter, setter)| {
1802 let getter = getter
1803 .map(|getter| quote! { Some(#getter) })
1804 .unwrap_or_else(|| quote! { None });
1805 let setter = setter
1806 .map(|setter| quote! { Some(#setter) })
1807 .unwrap_or_else(|| quote! { None });
1808 quote! {
1809 static #ident: napi::bindgen_prelude::ClassAccessorDescriptor = napi::bindgen_prelude::ClassAccessorDescriptor {
1810 getter: #getter,
1811 setter: #setter,
1812 };
1813 }
1814 });
1815 let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
1816 Ok(quote! {
1817 #[allow(non_snake_case)]
1818 #[allow(clippy::all)]
1819 mod #mod_name {
1820 use super::*;
1821 #(#accessor_descriptors)*
1822 #(#methods)*
1823
1824 #[cfg(all(not(test), not(target_family = "wasm")))]
1825 napi::ctor::declarative::ctor! {
1826 #[ctor(unsafe)]
1827 fn #register_name() {
1828 napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*], false);
1829 }
1830 }
1831
1832 #[cfg(all(not(test), target_family = "wasm"))]
1833 #[no_mangle]
1834 extern "C" fn #register_name() {
1835 napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props_wasm),*], false);
1836 }
1837 }
1838 })
1839 }
1840}
1841
1842pub fn rm_raw_prefix(s: &str) -> &str {
1843 if let Some(stripped) = s.strip_prefix("r#") {
1844 stripped
1845 } else {
1846 s
1847 }
1848}
1849
1850fn remove_lifetime_in_type(ty: &mut syn::Type) {
1851 if let syn::Type::Path(syn::TypePath { path, .. }) = ty {
1852 path.segments.iter_mut().for_each(|segment| {
1853 if let syn::PathArguments::AngleBracketed(ref mut args) = segment.arguments {
1854 args.args.iter_mut().for_each(|arg| match arg {
1855 syn::GenericArgument::Type(ref mut ty) => {
1856 remove_lifetime_in_type(ty);
1857 }
1858 syn::GenericArgument::Lifetime(lifetime) => {
1859 lifetime.ident = Ident::new("_", lifetime.ident.span());
1860 }
1861 _ => {}
1862 });
1863 }
1864 });
1865 }
1866}