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