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