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