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