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