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