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
19fn gen_napi_value_map_impl(
21 name: &Ident,
22 to_napi_val_impl: TokenStream,
23 has_lifetime: bool,
24) -> TokenStream {
25 let name_str = name.to_string();
26 let name = if has_lifetime {
27 quote! { #name<'_> }
28 } else {
29 quote! { #name }
30 };
31 let js_name_str = format!("{name_str}\0");
32 let validate = quote! {
33 unsafe fn validate(env: napi::sys::napi_env, napi_val: napi::sys::napi_value) -> napi::Result<napi::sys::napi_value> {
34 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
35 let mut ctor = std::ptr::null_mut();
36 napi::check_status!(
37 napi::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
38 "Failed to get constructor reference of class `{}`",
39 #name_str
40 )?;
41 let mut is_instance_of = false;
42 napi::check_status!(
43 napi::sys::napi_instanceof(env, napi_val, ctor, &mut is_instance_of),
44 "Failed to get external value of class `{}`",
45 #name_str
46 )?;
47 if is_instance_of {
48 Ok(std::ptr::null_mut())
49 } else {
50 Err(napi::Error::new(
51 napi::Status::InvalidArg,
52 format!("Value is not instanceof class `{}`", #name_str)
53 ))
54 }
55 } else {
56 Err(napi::Error::new(
57 napi::Status::InvalidArg,
58 format!("Failed to get constructor of class `{}`", #name_str)
59 ))
60 }
61 }
62 };
63 quote! {
64 #[automatically_derived]
65 impl napi::bindgen_prelude::TypeName for #name {
66 fn type_name() -> &'static str {
67 #name_str
68 }
69
70 fn value_type() -> napi::ValueType {
71 napi::ValueType::Function
72 }
73 }
74
75 #[automatically_derived]
76 impl napi::bindgen_prelude::TypeName for &#name {
77 fn type_name() -> &'static str {
78 #name_str
79 }
80
81 fn value_type() -> napi::ValueType {
82 napi::ValueType::Object
83 }
84 }
85
86 #[automatically_derived]
87 impl napi::bindgen_prelude::TypeName for &mut #name {
88 fn type_name() -> &'static str {
89 #name_str
90 }
91
92 fn value_type() -> napi::ValueType {
93 napi::ValueType::Object
94 }
95 }
96
97 #to_napi_val_impl
98
99 #[automatically_derived]
100 impl napi::bindgen_prelude::FromNapiRef for #name {
101 unsafe fn from_napi_ref(
102 env: napi::bindgen_prelude::sys::napi_env,
103 napi_val: napi::bindgen_prelude::sys::napi_value
104 ) -> napi::bindgen_prelude::Result<&'static Self> {
105 let mut wrapped_val: *mut std::ffi::c_void = std::ptr::null_mut();
106
107 napi::bindgen_prelude::check_status!(
108 napi::bindgen_prelude::sys::napi_unwrap(env, napi_val, &mut wrapped_val),
109 "Failed to recover `{}` type from napi value",
110 #name_str,
111 )?;
112
113 Ok(&*(wrapped_val as *const #name))
114 }
115 }
116
117 #[automatically_derived]
118 impl napi::bindgen_prelude::FromNapiMutRef for #name {
119 unsafe fn from_napi_mut_ref(
120 env: napi::bindgen_prelude::sys::napi_env,
121 napi_val: napi::bindgen_prelude::sys::napi_value
122 ) -> napi::bindgen_prelude::Result<&'static mut Self> {
123 let mut wrapped_val: *mut std::ffi::c_void = std::ptr::null_mut();
124
125 napi::bindgen_prelude::check_status!(
126 napi::bindgen_prelude::sys::napi_unwrap(env, napi_val, &mut wrapped_val),
127 "Failed to recover `{}` type from napi value",
128 #name_str,
129 )?;
130
131 Ok(&mut *(wrapped_val as *mut #name))
132 }
133 }
134
135 #[automatically_derived]
136 impl napi::bindgen_prelude::ValidateNapiValue for &#name {
137 #validate
138 }
139
140 #[automatically_derived]
141 impl napi::bindgen_prelude::ValidateNapiValue for &mut #name {
142 #validate
143 }
144 }
145}
146
147impl TryToTokens for NapiStruct {
148 fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
149 let napi_value_map_impl = self.gen_napi_value_map_impl();
150
151 let class_helper_mod = match &self.kind {
152 NapiStructKind::Class(class) => self.gen_helper_mod(class),
153 _ => quote! {},
154 };
155
156 (quote! {
157 #napi_value_map_impl
158 #class_helper_mod
159 })
160 .to_tokens(tokens);
161
162 Ok(())
163 }
164}
165
166impl NapiStruct {
167 fn gen_helper_mod(&self, class: &NapiClass) -> TokenStream {
168 let mod_name = Ident::new(&format!("__napi_helper__{}", self.name), Span::call_site());
169
170 let ctor = if class.ctor {
171 self.gen_default_ctor(class)
172 } else {
173 quote! {}
174 };
175
176 let mut getters_setters = self.gen_default_getters_setters(class);
177 getters_setters.sort_by(|a, b| a.0.cmp(&b.0));
178 let register = self.gen_register(class);
179
180 let getters_setters_token = getters_setters.into_iter().map(|(_, token)| token);
181
182 quote! {
183 #[allow(clippy::all)]
184 #[allow(non_snake_case)]
185 mod #mod_name {
186 use std::ptr;
187 use super::*;
188
189 #ctor
190 #(#getters_setters_token)*
191 #register
192 }
193 }
194 }
195
196 fn gen_default_ctor(&self, class: &NapiClass) -> TokenStream {
197 let name = &self.name;
198 let js_name_str = &self.js_name;
199 let fields_len = class.fields.len();
200 let mut fields = vec![];
201
202 for (i, field) in class.fields.iter().enumerate() {
203 let ty = &field.ty;
204 match &field.name {
205 syn::Member::Named(ident) => fields
206 .push(quote! { #ident: <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#i))? }),
207 syn::Member::Unnamed(_) => {
208 fields.push(quote! { <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb.get_arg(#i))? });
209 }
210 }
211 }
212
213 let construct = if class.is_tuple {
214 quote! { #name (#(#fields),*) }
215 } else {
216 quote! { #name {#(#fields),*} }
217 };
218
219 let is_empty_struct_hint = fields_len == 0;
220
221 let constructor = if class.implement_iterator {
222 quote! { unsafe { cb.construct_generator::<#is_empty_struct_hint, #name>(#js_name_str, #construct) } }
223 } else {
224 quote! { unsafe { cb.construct::<#is_empty_struct_hint, #name>(#js_name_str, #construct) } }
225 };
226
227 quote! {
228 extern "C" fn constructor(
229 env: napi::bindgen_prelude::sys::napi_env,
230 cb: napi::bindgen_prelude::sys::napi_callback_info
231 ) -> napi::bindgen_prelude::sys::napi_value {
232 napi::bindgen_prelude::CallbackInfo::<#fields_len>::new(env, cb, None, false)
233 .and_then(|cb| #constructor)
234 .unwrap_or_else(|e| {
235 unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
236 std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
237 })
238 }
239 }
240 }
241
242 fn gen_napi_value_map_impl(&self) -> TokenStream {
243 match &self.kind {
244 NapiStructKind::Array(array) => self.gen_napi_value_array_impl(array),
245 NapiStructKind::Transparent(transparent) => self.gen_napi_value_transparent_impl(transparent),
246 NapiStructKind::Class(class) if !class.ctor => gen_napi_value_map_impl(
247 &self.name,
248 self.gen_to_napi_value_ctor_impl_for_non_default_constructor_struct(class),
249 self.has_lifetime,
250 ),
251 NapiStructKind::Class(class) => gen_napi_value_map_impl(
252 &self.name,
253 self.gen_to_napi_value_ctor_impl(class),
254 self.has_lifetime,
255 ),
256 NapiStructKind::Object(obj) => self.gen_to_napi_value_obj_impl(obj),
257 NapiStructKind::StructuredEnum(structured_enum) => {
258 self.gen_to_napi_value_structured_enum_impl(structured_enum)
259 }
260 }
261 }
262
263 fn gen_to_napi_value_ctor_impl_for_non_default_constructor_struct(
264 &self,
265 class: &NapiClass,
266 ) -> TokenStream {
267 let name = &self.name;
268 let js_name_raw = &self.js_name;
269 let js_name_str = format!("{js_name_raw}\0");
270 let iterator_implementation = self.gen_iterator_property(class, name);
271 let (object_finalize_impl, to_napi_value_impl, javascript_class_ext_impl) = if self.has_lifetime
272 {
273 let name = quote! { #name<'_javascript_function_scope> };
274 (
275 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ObjectFinalize for #name {} },
276 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name },
277 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::JavaScriptClassExt for #name },
278 )
279 } else {
280 (
281 quote! { impl napi::bindgen_prelude::ObjectFinalize for #name {} },
282 quote! { impl napi::bindgen_prelude::ToNapiValue for #name },
283 quote! { impl napi::bindgen_prelude::JavaScriptClassExt for #name },
284 )
285 };
286 let finalize_trait = if class.use_custom_finalize {
287 quote! {}
288 } else {
289 quote! {
290 #[automatically_derived]
291 #object_finalize_impl
292 }
293 };
294 quote! {
295 #[automatically_derived]
296 #to_napi_value_impl {
297 unsafe fn to_napi_value(
298 env: napi::sys::napi_env,
299 val: #name
300 ) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
301 if let Some(ctor_ref) = napi::__private::get_class_constructor(#js_name_str) {
302 let mut wrapped_value = Box::into_raw(Box::new(val));
303 if wrapped_value as usize == 0x1 {
304 wrapped_value = Box::into_raw(Box::new(0u8)).cast();
305 }
306 let instance_value = napi::bindgen_prelude::new_instance::<#name>(env, wrapped_value.cast(), ctor_ref)?;
307 #iterator_implementation
308 Ok(instance_value)
309 } else {
310 Err(napi::bindgen_prelude::Error::new(
311 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}` in `ToNapiValue`", #js_name_raw))
312 )
313 }
314 }
315 }
316
317 #finalize_trait
318
319 #[automatically_derived]
320 #javascript_class_ext_impl {
321 fn into_instance<'scope>(self, env: &'scope napi::Env) -> napi::Result<napi::bindgen_prelude::ClassInstance<'scope, Self>>
322 {
323 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
324 unsafe {
325 let wrapped_value = Box::into_raw(Box::new(self));
326 let instance_value = napi::bindgen_prelude::new_instance::<#name>(env.raw(), wrapped_value as *mut _ as *mut std::ffi::c_void, ctor_ref)?;
327 Ok(napi::bindgen_prelude::ClassInstance::new(instance_value, env.raw(), wrapped_value))
328 }
329 } else {
330 Err(napi::bindgen_prelude::Error::new(
331 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
332 )
333 }
334 }
335
336 fn into_reference(self, env: napi::Env) -> napi::Result<napi::bindgen_prelude::Reference<Self>> {
337 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
338 unsafe {
339 let mut wrapped_value = Box::into_raw(Box::new(self));
340 if wrapped_value as usize == 0x1 {
341 wrapped_value = Box::into_raw(Box::new(0u8)).cast();
342 }
343 let instance_value = napi::bindgen_prelude::new_instance::<#name>(env.raw(), wrapped_value.cast(), ctor_ref)?;
344 {
345 let env = env.raw();
346 #iterator_implementation
347 }
348 napi::bindgen_prelude::Reference::<#name>::from_value_ptr(wrapped_value.cast(), env.raw())
349 }
350 } else {
351 Err(napi::bindgen_prelude::Error::new(
352 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
353 )
354 }
355 }
356
357 fn instance_of<'env, V: napi::JsValue<'env>>(env: &napi::bindgen_prelude::Env, value: &V) -> napi::bindgen_prelude::Result<bool> {
358 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
359 let mut ctor = std::ptr::null_mut();
360 napi::check_status!(
361 unsafe { napi::sys::napi_get_reference_value(env.raw(), ctor_ref, &mut ctor) },
362 "Failed to get constructor reference of class `{}`",
363 #js_name_str
364 )?;
365 let mut is_instance_of = false;
366 napi::check_status!(
367 unsafe { napi::sys::napi_instanceof(env.raw(), value.value().value, ctor, &mut is_instance_of) },
368 "Failed to run instanceof for class `{}`",
369 #js_name_str
370 )?;
371 Ok(is_instance_of)
372 } else {
373 Err(napi::Error::new(napi::Status::GenericFailure, format!("Failed to get constructor of class `{}`", #js_name_str)))
374 }
375 }
376 }
377 }
378 }
379
380 fn gen_iterator_property(&self, class: &NapiClass, name: &Ident) -> TokenStream {
381 if !class.implement_iterator {
382 return quote! {};
383 }
384 quote! {
385 unsafe { napi::__private::create_iterator::<#name>(env, instance_value, wrapped_value); }
386 }
387 }
388
389 fn gen_to_napi_value_ctor_impl(&self, class: &NapiClass) -> TokenStream {
390 let name = &self.name;
391 let js_name_without_null = &self.js_name;
392 let js_name_str = format!("{}\0", &self.js_name);
393
394 let mut field_conversions = vec![];
395 let mut field_destructions = vec![];
396
397 for field in class.fields.iter() {
398 let ty = &field.ty;
399
400 match &field.name {
401 syn::Member::Named(ident) => {
402 let alias_ident = format_ident!("{}_", ident);
404 field_destructions.push(quote! { #ident: #alias_ident });
405 field_conversions.push(
406 quote! { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #alias_ident)? },
407 );
408 }
409 syn::Member::Unnamed(i) => {
410 let arg_name = format_ident!("arg{}", i);
411 field_destructions.push(quote! { #arg_name });
412 field_conversions.push(
413 quote! { <#ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, #arg_name)? },
414 );
415 }
416 }
417 }
418
419 let destructed_fields = if class.is_tuple {
420 quote! {
421 Self (#(#field_destructions),*)
422 }
423 } else {
424 quote! {
425 Self {#(#field_destructions),*}
426 }
427 };
428
429 let finalize_trait = if class.use_custom_finalize {
430 quote! {}
431 } else if self.has_lifetime {
432 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ObjectFinalize for #name<'_javascript_function_scope> {} }
433 } else {
434 quote! { impl napi::bindgen_prelude::ObjectFinalize for #name {} }
435 };
436
437 let to_napi_value_impl = if self.has_lifetime {
438 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name<'_javascript_function_scope> }
439 } else {
440 quote! { impl napi::bindgen_prelude::ToNapiValue for #name }
441 };
442
443 quote! {
444 #[automatically_derived]
445 #to_napi_value_impl {
446 unsafe fn to_napi_value(
447 env: napi::bindgen_prelude::sys::napi_env,
448 val: #name,
449 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
450 if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
451 let mut ctor = std::ptr::null_mut();
452
453 napi::bindgen_prelude::check_status!(
454 napi::bindgen_prelude::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
455 "Failed to get constructor reference of class `{}`",
456 #js_name_without_null
457 )?;
458
459 let mut instance_value = std::ptr::null_mut();
460 let #destructed_fields = val;
461 let args = vec![#(#field_conversions),*];
462
463 napi::bindgen_prelude::check_status!(
464 napi::bindgen_prelude::sys::napi_new_instance(env, ctor, args.len(), args.as_ptr(), &mut instance_value),
465 "Failed to construct class `{}`",
466 #js_name_without_null
467 )?;
468
469 Ok(instance_value)
470 } else {
471 Err(napi::bindgen_prelude::Error::new(
472 napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_str))
473 )
474 }
475 }
476 }
477 #finalize_trait
478 }
479 }
480
481 fn gen_to_napi_value_obj_impl(&self, obj: &NapiObject) -> TokenStream {
482 let name = &self.name;
483 let name_str = self.name.to_string();
484
485 let mut obj_field_setters = vec![];
486 let mut obj_field_getters = vec![];
487 let mut field_destructions = vec![];
488
489 for field in obj.fields.iter() {
490 let field_js_name = &field.js_name;
491 let mut ty = field.ty.clone();
492 remove_lifetime_in_type(&mut ty);
493 let is_optional_field = if let syn::Type::Path(syn::TypePath {
494 path: syn::Path { segments, .. },
495 ..
496 }) = &ty
497 {
498 if let Some(last_path) = segments.last() {
499 last_path.ident == "Option"
500 } else {
501 false
502 }
503 } else {
504 false
505 };
506 match &field.name {
507 syn::Member::Named(ident) => {
508 let alias_ident = format_ident!("{}_", ident);
509 field_destructions.push(quote! { #ident: #alias_ident });
510 if is_optional_field {
511 obj_field_setters.push(match self.use_nullable {
512 false => quote! {
513 if #alias_ident.is_some() {
514 obj.set(#field_js_name, #alias_ident)?;
515 }
516 },
517 true => quote! {
518 if let Some(#alias_ident) = #alias_ident {
519 obj.set(#field_js_name, #alias_ident)?;
520 } else {
521 obj.set(#field_js_name, napi::bindgen_prelude::Null)?;
522 }
523 },
524 });
525 } else {
526 obj_field_setters.push(quote! { obj.set(#field_js_name, #alias_ident)?; });
527 }
528 if is_optional_field && !self.use_nullable {
529 obj_field_getters.push(quote! {
530 let #alias_ident: #ty = obj.get(#field_js_name).map_err(|mut err| {
531 err.reason = format!("{} on {}.{}", err.reason, #name_str, #field_js_name);
532 err
533 })?;
534 });
535 } else {
536 obj_field_getters.push(quote! {
537 let #alias_ident: #ty = obj.get(#field_js_name).map_err(|mut err| {
538 err.reason = format!("{} on {}.{}", err.reason, #name_str, #field_js_name);
539 err
540 })?.ok_or_else(|| napi::bindgen_prelude::Error::new(
541 napi::bindgen_prelude::Status::InvalidArg,
542 format!("Missing field `{}`", #field_js_name),
543 ))?;
544 });
545 }
546 }
547 syn::Member::Unnamed(i) => {
548 let arg_name = format_ident!("arg{}", i);
549 field_destructions.push(quote! { #arg_name });
550 if is_optional_field {
551 obj_field_setters.push(match self.use_nullable {
552 false => quote! {
553 if #arg_name.is_some() {
554 obj.set(#field_js_name, #arg_name)?;
555 }
556 },
557 true => quote! {
558 if let Some(#arg_name) = #arg_name {
559 obj.set(#field_js_name, #arg_name)?;
560 } else {
561 obj.set(#field_js_name, napi::bindgen_prelude::Null)?;
562 }
563 },
564 });
565 } else {
566 obj_field_setters.push(quote! { obj.set(#field_js_name, #arg_name)?; });
567 }
568 if is_optional_field && !self.use_nullable {
569 obj_field_getters.push(quote! { let #arg_name: #ty = obj.get(#field_js_name)?; });
570 } else {
571 obj_field_getters.push(quote! {
572 let #arg_name: #ty = obj.get(#field_js_name)?.ok_or_else(|| napi::bindgen_prelude::Error::new(
573 napi::bindgen_prelude::Status::InvalidArg,
574 format!("Missing field `{}`", #field_js_name),
575 ))?;
576 });
577 }
578 }
579 }
580 }
581
582 let destructed_fields = if obj.is_tuple {
583 quote! {
584 Self (#(#field_destructions),*)
585 }
586 } else {
587 quote! {
588 Self {#(#field_destructions),*}
589 }
590 };
591
592 let name_with_lifetime = if self.has_lifetime {
593 quote! { #name<'_javascript_function_scope> }
594 } else {
595 quote! { #name }
596 };
597 let (from_napi_value_impl, to_napi_value_impl, validate_napi_value_impl, type_name_impl) =
598 if self.has_lifetime {
599 (
600 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::FromNapiValue for #name<'_javascript_function_scope> },
601 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name<'_javascript_function_scope> },
602 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ValidateNapiValue for #name<'_javascript_function_scope> },
603 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::TypeName for #name<'_javascript_function_scope> },
604 )
605 } else {
606 (
607 quote! { impl napi::bindgen_prelude::FromNapiValue for #name },
608 quote! { impl napi::bindgen_prelude::ToNapiValue for #name },
609 quote! { impl napi::bindgen_prelude::ValidateNapiValue for #name },
610 quote! { impl napi::bindgen_prelude::TypeName for #name },
611 )
612 };
613
614 let to_napi_value = if obj.object_to_js {
615 quote! {
616 #[automatically_derived]
617 #to_napi_value_impl {
618 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> {
619 #[allow(unused_variables)]
620 let env_wrapper = napi::bindgen_prelude::Env::from(env);
621 #[allow(unused_mut)]
622 let mut obj = napi::bindgen_prelude::Object::new(&env_wrapper)?;
623
624 let #destructed_fields = val;
625 #(#obj_field_setters)*
626
627 napi::bindgen_prelude::Object::to_napi_value(env, obj)
628 }
629 }
630 }
631 } else {
632 quote! {}
633 };
634
635 let from_napi_value = if obj.object_from_js {
636 let return_type = if self.has_lifetime {
637 quote! { #name<'_javascript_function_scope> }
638 } else {
639 quote! { #name }
640 };
641 quote! {
642 #[automatically_derived]
643 #from_napi_value_impl {
644 unsafe fn from_napi_value(
645 env: napi::bindgen_prelude::sys::napi_env,
646 napi_val: napi::bindgen_prelude::sys::napi_value
647 ) -> napi::bindgen_prelude::Result<#return_type> {
648 #[allow(unused_variables)]
649 let env_wrapper = napi::bindgen_prelude::Env::from(env);
650 #[allow(unused_mut)]
651 let mut obj = napi::bindgen_prelude::Object::from_napi_value(env, napi_val)?;
652
653 #(#obj_field_getters)*
654
655 let val = #destructed_fields;
656
657 Ok(val)
658 }
659 }
660
661 #[automatically_derived]
662 #validate_napi_value_impl {}
663 }
664 } else {
665 quote! {}
666 };
667
668 quote! {
669 #[automatically_derived]
670 #type_name_impl {
671 fn type_name() -> &'static str {
672 #name_str
673 }
674
675 fn value_type() -> napi::ValueType {
676 napi::ValueType::Object
677 }
678 }
679
680 #to_napi_value
681
682 #from_napi_value
683 }
684 }
685
686 fn gen_default_getters_setters(&self, class: &NapiClass) -> Vec<(String, TokenStream)> {
687 let mut getters_setters = vec![];
688 let struct_name = &self.name;
689
690 for field in class.fields.iter() {
691 let field_ident = &field.name;
692 let field_name = match &field.name {
693 syn::Member::Named(ident) => ident.to_string(),
694 syn::Member::Unnamed(i) => format!("field{}", i.index),
695 };
696 let ty = &field.ty;
697
698 let getter_name = Ident::new(
699 &format!("get_{}", rm_raw_prefix(&field_name)),
700 Span::call_site(),
701 );
702 let setter_name = Ident::new(
703 &format!("set_{}", rm_raw_prefix(&field_name)),
704 Span::call_site(),
705 );
706
707 if field.getter {
708 let default_to_napi_value_convert = quote! {
709 let val = &mut obj.#field_ident;
710 unsafe { <&mut #ty as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, val) }
711 };
712 let to_napi_value_convert = if let syn::Type::Path(syn::TypePath {
713 path: syn::Path { segments, .. },
714 ..
715 }) = ty
716 {
717 if let Some(syn::PathSegment { ident, .. }) = segments.last() {
718 if STRUCT_FIELD_SPECIAL_CASE.iter().any(|name| ident == name) {
719 quote! {
720 let val = obj.#field_ident.as_mut();
721 unsafe { napi::bindgen_prelude::ToNapiValue::to_napi_value(env, val) }
722 }
723 } else {
724 default_to_napi_value_convert
725 }
726 } else {
727 default_to_napi_value_convert
728 }
729 } else {
730 default_to_napi_value_convert
731 };
732 getters_setters.push((
733 field.js_name.clone(),
734 quote! {
735 extern "C" fn #getter_name(
736 env: napi::bindgen_prelude::sys::napi_env,
737 cb: napi::bindgen_prelude::sys::napi_callback_info
738 ) -> napi::bindgen_prelude::sys::napi_value {
739 napi::bindgen_prelude::CallbackInfo::<0>::new(env, cb, Some(0), false)
740 .and_then(|mut cb| cb.unwrap_borrow_mut::<#struct_name>())
741 .and_then(|obj| {
742 #to_napi_value_convert
743 })
744 .unwrap_or_else(|e| {
745 unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
746 std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
747 })
748 }
749 },
750 ));
751 }
752
753 if field.setter {
754 getters_setters.push((
755 field.js_name.clone(),
756 quote! {
757 extern "C" fn #setter_name(
758 env: napi::bindgen_prelude::sys::napi_env,
759 cb: napi::bindgen_prelude::sys::napi_callback_info
760 ) -> napi::bindgen_prelude::sys::napi_value {
761 napi::bindgen_prelude::CallbackInfo::<1>::new(env, cb, Some(1), false)
762 .and_then(|mut cb_info| unsafe {
763 cb_info.unwrap_borrow_mut::<#struct_name>()
764 .and_then(|obj| {
765 <#ty as napi::bindgen_prelude::FromNapiValue>::from_napi_value(env, cb_info.get_arg(0))
766 .and_then(move |val| {
767 obj.#field_ident = val;
768 <() as napi::bindgen_prelude::ToNapiValue>::to_napi_value(env, ())
769 })
770 })
771 })
772 .unwrap_or_else(|e| {
773 unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
774 std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
775 })
776 }
777 },
778 ));
779 }
780 }
781
782 getters_setters
783 }
784
785 fn gen_register(&self, class: &NapiClass) -> TokenStream {
786 let name = &self.name;
787 let struct_register_name = &self.register_name;
788 let js_name = format!("{}\0", self.js_name);
789 let mut props = vec![];
790
791 if class.ctor {
792 props.push(quote! { napi::bindgen_prelude::Property::new().with_utf8_name("constructor").unwrap().with_ctor(constructor) });
793 }
794
795 for field in class.fields.iter() {
796 let field_name = match &field.name {
797 syn::Member::Named(ident) => ident.to_string(),
798 syn::Member::Unnamed(i) => format!("field{}", i.index),
799 };
800
801 if !field.getter {
802 continue;
803 }
804
805 let js_name = &field.js_name;
806 let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT;
807 if field.writable {
808 attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE;
809 }
810 if field.enumerable {
811 attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE;
812 }
813 if field.configurable {
814 attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE;
815 }
816
817 let mut prop = quote! {
818 napi::bindgen_prelude::Property::new().with_utf8_name(#js_name)
819 .unwrap()
820 .with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap())
821 };
822
823 if field.getter {
824 let getter_name = Ident::new(
825 &format!("get_{}", rm_raw_prefix(&field_name)),
826 Span::call_site(),
827 );
828 (quote! { .with_getter(#getter_name) }).to_tokens(&mut prop);
829 }
830
831 if field.writable && field.setter {
832 let setter_name = Ident::new(
833 &format!("set_{}", rm_raw_prefix(&field_name)),
834 Span::call_site(),
835 );
836 (quote! { .with_setter(#setter_name) }).to_tokens(&mut prop);
837 }
838
839 props.push(prop);
840 }
841 let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
842 quote! {
843 #[allow(non_snake_case)]
844 #[allow(clippy::all)]
845 #[cfg(all(not(test), not(target_family = "wasm")))]
846 #[napi::ctor::ctor(crate_path=napi::ctor)]
847 fn #struct_register_name() {
848 napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*]);
849 }
850
851 #[allow(non_snake_case)]
852 #[allow(clippy::all)]
853 #[cfg(all(not(test), target_family = "wasm"))]
854 #[no_mangle]
855 extern "C" fn #struct_register_name() {
856 napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*]);
857 }
858 }
859 }
860
861 fn gen_to_napi_value_structured_enum_impl(
862 &self,
863 structured_enum: &NapiStructuredEnum,
864 ) -> TokenStream {
865 let name = &self.name;
866 let name_str = self.name.to_string();
867 let discriminant = structured_enum.discriminant.as_str();
868
869 let mut variant_arm_setters = vec![];
870 let mut variant_arm_getters = vec![];
871
872 for variant in structured_enum.variants.iter() {
873 let variant_name = &variant.name;
874 let mut variant_name_str = variant_name.to_string();
875 if let Some(case) = structured_enum.discriminant_case {
876 variant_name_str = to_case(variant_name_str, case);
877 }
878 let mut obj_field_setters = vec![quote! {
879 obj.set(#discriminant, #variant_name_str)?;
880 }];
881 let mut obj_field_getters = vec![];
882 let mut field_destructions = vec![];
883 for field in variant.fields.iter() {
884 let field_js_name = &field.js_name;
885 let mut ty = field.ty.clone();
886 remove_lifetime_in_type(&mut ty);
887 let is_optional_field = if let syn::Type::Path(syn::TypePath {
888 path: syn::Path { segments, .. },
889 ..
890 }) = &ty
891 {
892 if let Some(last_path) = segments.last() {
893 last_path.ident == "Option"
894 } else {
895 false
896 }
897 } else {
898 false
899 };
900 match &field.name {
901 syn::Member::Named(ident) => {
902 let alias_ident = format_ident!("{}_", ident);
903 field_destructions.push(quote! { #ident: #alias_ident });
904 if is_optional_field {
905 obj_field_setters.push(match self.use_nullable {
906 false => quote! {
907 if #alias_ident.is_some() {
908 obj.set(#field_js_name, #alias_ident)?;
909 }
910 },
911 true => quote! {
912 if let Some(#alias_ident) = #alias_ident {
913 obj.set(#field_js_name, #alias_ident)?;
914 } else {
915 obj.set(#field_js_name, napi::bindgen_prelude::Null)?;
916 }
917 },
918 });
919 } else {
920 obj_field_setters.push(quote! { obj.set(#field_js_name, #alias_ident)?; });
921 }
922 if is_optional_field && !self.use_nullable {
923 obj_field_getters.push(quote! {
924 let #alias_ident: #ty = obj.get(#field_js_name).map_err(|mut err| {
925 err.reason = format!("{} on {}.{}", err.reason, #name_str, #field_js_name);
926 err
927 })?;
928 });
929 } else {
930 obj_field_getters.push(quote! {
931 let #alias_ident: #ty = obj.get(#field_js_name).map_err(|mut err| {
932 err.reason = format!("{} on {}.{}", err.reason, #name_str, #field_js_name);
933 err
934 })?.ok_or_else(|| napi::bindgen_prelude::Error::new(
935 napi::bindgen_prelude::Status::InvalidArg,
936 format!("Missing field `{}`", #field_js_name),
937 ))?;
938 });
939 }
940 }
941 syn::Member::Unnamed(i) => {
942 let arg_name = format_ident!("arg{}", i);
943 field_destructions.push(quote! { #arg_name });
944 if is_optional_field {
945 obj_field_setters.push(match self.use_nullable {
946 false => quote! {
947 if #arg_name.is_some() {
948 obj.set(#field_js_name, #arg_name)?;
949 }
950 },
951 true => quote! {
952 if let Some(#arg_name) = #arg_name {
953 obj.set(#field_js_name, #arg_name)?;
954 } else {
955 obj.set(#field_js_name, napi::bindgen_prelude::Null)?;
956 }
957 },
958 });
959 } else {
960 obj_field_setters.push(quote! { obj.set(#field_js_name, #arg_name)?; });
961 }
962 if is_optional_field && !self.use_nullable {
963 obj_field_getters.push(quote! { let #arg_name: #ty = obj.get(#field_js_name)?; });
964 } else {
965 obj_field_getters.push(quote! {
966 let #arg_name: #ty = obj.get(#field_js_name)?.ok_or_else(|| napi::bindgen_prelude::Error::new(
967 napi::bindgen_prelude::Status::InvalidArg,
968 format!("Missing field `{}`", #field_js_name),
969 ))?;
970 });
971 }
972 }
973 }
974 }
975
976 let destructed_fields = if variant.is_tuple {
977 quote! {
978 Self::#variant_name (#(#field_destructions),*)
979 }
980 } else {
981 quote! {
982 Self::#variant_name {#(#field_destructions),*}
983 }
984 };
985
986 variant_arm_setters.push(quote! {
987 #destructed_fields => {
988 #(#obj_field_setters)*
989 },
990 });
991
992 variant_arm_getters.push(quote! {
993 #variant_name_str => {
994 #(#obj_field_getters)*
995 #destructed_fields
996 },
997 })
998 }
999
1000 let to_napi_value = if structured_enum.object_to_js {
1001 quote! {
1002 impl napi::bindgen_prelude::ToNapiValue for #name {
1003 unsafe fn to_napi_value(env: napi::bindgen_prelude::sys::napi_env, val: #name) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
1004 #[allow(unused_variables)]
1005 let env_wrapper = napi::bindgen_prelude::Env::from(env);
1006 #[allow(unused_mut)]
1007 let mut obj = napi::bindgen_prelude::Object::new(&env_wrapper)?;
1008 match val {
1009 #(#variant_arm_setters)*
1010 };
1011
1012 napi::bindgen_prelude::Object::to_napi_value(env, obj)
1013 }
1014 }
1015 }
1016 } else {
1017 quote! {}
1018 };
1019
1020 let from_napi_value = if structured_enum.object_from_js {
1021 quote! {
1022 impl napi::bindgen_prelude::FromNapiValue for #name {
1023 unsafe fn from_napi_value(
1024 env: napi::bindgen_prelude::sys::napi_env,
1025 napi_val: napi::bindgen_prelude::sys::napi_value
1026 ) -> napi::bindgen_prelude::Result<Self> {
1027 #[allow(unused_variables)]
1028 let env_wrapper = napi::bindgen_prelude::Env::from(env);
1029 #[allow(unused_mut)]
1030 let mut obj = napi::bindgen_prelude::Object::from_napi_value(env, napi_val)?;
1031 let type_: String = obj.get(#discriminant).map_err(|mut err| {
1032 err.reason = format!("{} on {}.{}", err.reason, #name_str, #discriminant);
1033 err
1034 })?.ok_or_else(|| napi::bindgen_prelude::Error::new(
1035 napi::bindgen_prelude::Status::InvalidArg,
1036 format!("Missing field `{}`", #discriminant),
1037 ))?;
1038 let val = match type_.as_str() {
1039 #(#variant_arm_getters)*
1040 _ => return Err(napi::bindgen_prelude::Error::new(
1041 napi::bindgen_prelude::Status::InvalidArg,
1042 format!("Unknown variant `{}`", type_),
1043 )),
1044 };
1045
1046 Ok(val)
1047 }
1048 }
1049
1050 impl napi::bindgen_prelude::ValidateNapiValue for #name {}
1051 }
1052 } else {
1053 quote! {}
1054 };
1055
1056 quote! {
1057 impl napi::bindgen_prelude::TypeName for #name {
1058 fn type_name() -> &'static str {
1059 #name_str
1060 }
1061
1062 fn value_type() -> napi::ValueType {
1063 napi::ValueType::Object
1064 }
1065 }
1066
1067 #to_napi_value
1068
1069 #from_napi_value
1070 }
1071 }
1072
1073 fn gen_napi_value_transparent_impl(&self, transparent: &NapiTransparent) -> TokenStream {
1074 let name = &self.name;
1075 let name = if self.has_lifetime {
1076 quote! { #name<'_> }
1077 } else {
1078 quote! { #name }
1079 };
1080 let inner_type = transparent.ty.clone().into_token_stream();
1081
1082 let to_napi_value = if transparent.object_to_js {
1083 quote! {
1084 #[automatically_derived]
1085 impl napi::bindgen_prelude::ToNapiValue for #name {
1086 unsafe fn to_napi_value(
1087 env: napi::bindgen_prelude::sys::napi_env,
1088 val: Self
1089 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
1090 <#inner_type>::to_napi_value(env, val.0)
1091 }
1092 }
1093 }
1094 } else {
1095 quote! {}
1096 };
1097
1098 let from_napi_value = if transparent.object_from_js {
1099 quote! {
1100 #[automatically_derived]
1101 impl napi::bindgen_prelude::FromNapiValue for #name {
1102 unsafe fn from_napi_value(
1103 env: napi::bindgen_prelude::sys::napi_env,
1104 napi_val: napi::bindgen_prelude::sys::napi_value
1105 ) -> napi::bindgen_prelude::Result<Self> {
1106 Ok(Self(<#inner_type>::from_napi_value(env, napi_val)?))
1107 }
1108 }
1109 }
1110 } else {
1111 quote! {}
1112 };
1113
1114 quote! {
1115 #[automatically_derived]
1116 impl napi::bindgen_prelude::TypeName for #name {
1117 fn type_name() -> &'static str {
1118 <#inner_type>::type_name()
1119 }
1120
1121 fn value_type() -> napi::ValueType {
1122 <#inner_type>::value_type()
1123 }
1124 }
1125
1126 #[automatically_derived]
1127 impl napi::bindgen_prelude::ValidateNapiValue for #name {
1128 unsafe fn validate(
1129 env: napi::bindgen_prelude::sys::napi_env,
1130 napi_val: napi::bindgen_prelude::sys::napi_value
1131 ) -> napi::bindgen_prelude::Result<napi::sys::napi_value> {
1132 <#inner_type>::validate(env, napi_val)
1133 }
1134 }
1135
1136 #to_napi_value
1137
1138 #from_napi_value
1139 }
1140 }
1141
1142 fn gen_napi_value_array_impl(&self, array: &NapiArray) -> TokenStream {
1143 let name = &self.name;
1144 let name_str = self.name.to_string();
1145
1146 let mut obj_field_setters = vec![];
1147 let mut obj_field_getters = vec![];
1148 let mut field_destructions = vec![];
1149
1150 for field in array.fields.iter() {
1151 let mut ty = field.ty.clone();
1152 remove_lifetime_in_type(&mut ty);
1153 let is_optional_field = if let syn::Type::Path(syn::TypePath {
1154 path: syn::Path { segments, .. },
1155 ..
1156 }) = &ty
1157 {
1158 if let Some(last_path) = segments.last() {
1159 last_path.ident == "Option"
1160 } else {
1161 false
1162 }
1163 } else {
1164 false
1165 };
1166
1167 if let syn::Member::Unnamed(i) = &field.name {
1168 let arg_name = format_ident!("arg{}", i);
1169 let field_index = i.index;
1170 field_destructions.push(quote! { #arg_name });
1171 if is_optional_field {
1172 obj_field_setters.push(match self.use_nullable {
1173 false => quote! {
1174 if #arg_name.is_some() {
1175 array.set(#field_index, #arg_name)?;
1176 }
1177 },
1178 true => quote! {
1179 if let Some(#arg_name) = #arg_name {
1180 array.set(#field_index, #arg_name)?;
1181 } else {
1182 array.set(#field_index, napi::bindgen_prelude::Null)?;
1183 }
1184 },
1185 });
1186 } else {
1187 obj_field_setters.push(quote! { array.set(#field_index, #arg_name)?; });
1188 }
1189 if is_optional_field && !self.use_nullable {
1190 obj_field_getters.push(quote! { let #arg_name: #ty = array.get(#field_index)?; });
1191 } else {
1192 obj_field_getters.push(quote! {
1193 let #arg_name: #ty = array.get(#field_index)?.ok_or_else(|| napi::bindgen_prelude::Error::new(
1194 napi::bindgen_prelude::Status::InvalidArg,
1195 format!("Failed to get element with index `{}`", #field_index),
1196 ))?;
1197 });
1198 }
1199 }
1200 }
1201
1202 let destructed_fields = quote! {
1203 Self (#(#field_destructions),*)
1204 };
1205
1206 let name_with_lifetime = if self.has_lifetime {
1207 quote! { #name<'_javascript_function_scope> }
1208 } else {
1209 quote! { #name }
1210 };
1211 let (from_napi_value_impl, to_napi_value_impl, validate_napi_value_impl, type_name_impl) =
1212 if self.has_lifetime {
1213 (
1214 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::FromNapiValue for #name<'_javascript_function_scope> },
1215 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ToNapiValue for #name<'_javascript_function_scope> },
1216 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::ValidateNapiValue for #name<'_javascript_function_scope> },
1217 quote! { impl <'_javascript_function_scope> napi::bindgen_prelude::TypeName for #name<'_javascript_function_scope> },
1218 )
1219 } else {
1220 (
1221 quote! { impl napi::bindgen_prelude::FromNapiValue for #name },
1222 quote! { impl napi::bindgen_prelude::ToNapiValue for #name },
1223 quote! { impl napi::bindgen_prelude::ValidateNapiValue for #name },
1224 quote! { impl napi::bindgen_prelude::TypeName for #name },
1225 )
1226 };
1227
1228 let array_len = array.fields.len() as u32;
1229
1230 let to_napi_value = if array.object_to_js {
1231 quote! {
1232 #[automatically_derived]
1233 #to_napi_value_impl {
1234 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> {
1235 #[allow(unused_variables)]
1236 let env_wrapper = napi::bindgen_prelude::Env::from(env);
1237 #[allow(unused_mut)]
1238 let mut array = env_wrapper.create_array(#array_len)?;
1239
1240 let #destructed_fields = val;
1241 #(#obj_field_setters)*
1242
1243 napi::bindgen_prelude::Array::to_napi_value(env, array)
1244 }
1245 }
1246 }
1247 } else {
1248 quote! {}
1249 };
1250
1251 let from_napi_value = if array.object_from_js {
1252 let return_type = if self.has_lifetime {
1253 quote! { #name<'_javascript_function_scope> }
1254 } else {
1255 quote! { #name }
1256 };
1257 quote! {
1258 #[automatically_derived]
1259 #from_napi_value_impl {
1260 unsafe fn from_napi_value(
1261 env: napi::bindgen_prelude::sys::napi_env,
1262 napi_val: napi::bindgen_prelude::sys::napi_value
1263 ) -> napi::bindgen_prelude::Result<#return_type> {
1264 #[allow(unused_variables)]
1265 let env_wrapper = napi::bindgen_prelude::Env::from(env);
1266 #[allow(unused_mut)]
1267 let mut array = napi::bindgen_prelude::Array::from_napi_value(env, napi_val)?;
1268
1269 #(#obj_field_getters)*
1270
1271 let val = #destructed_fields;
1272
1273 Ok(val)
1274 }
1275 }
1276
1277 #[automatically_derived]
1278 #validate_napi_value_impl {}
1279 }
1280 } else {
1281 quote! {}
1282 };
1283
1284 quote! {
1285 #[automatically_derived]
1286 #type_name_impl {
1287 fn type_name() -> &'static str {
1288 #name_str
1289 }
1290
1291 fn value_type() -> napi::ValueType {
1292 napi::ValueType::Object
1293 }
1294 }
1295
1296 #to_napi_value
1297
1298 #from_napi_value
1299 }
1300 }
1301}
1302
1303impl TryToTokens for NapiImpl {
1304 fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
1305 self.gen_helper_mod()?.to_tokens(tokens);
1306
1307 Ok(())
1308 }
1309}
1310
1311impl NapiImpl {
1312 fn gen_helper_mod(&self) -> BindgenResult<TokenStream> {
1313 if cfg!(test) {
1314 return Ok(quote! {});
1315 }
1316
1317 let name = &self.name;
1318 let name_str = self.name.to_string();
1319 let js_name = format!("{}\0", self.js_name);
1320 let mod_name = Ident::new(
1321 &format!(
1322 "__napi_impl_helper_{}_{}",
1323 name_str,
1324 NAPI_IMPL_ID.fetch_add(1, Ordering::SeqCst)
1325 ),
1326 Span::call_site(),
1327 );
1328
1329 let register_name = &self.register_name;
1330
1331 let mut methods = vec![];
1332 let mut props = HashMap::new();
1333
1334 for item in self.items.iter() {
1335 let js_name = Literal::string(&item.js_name);
1336 let item_str = item.name.to_string();
1337 let intermediate_name = get_intermediate_ident(&item_str);
1338 methods.push(item.try_to_token_stream()?);
1339
1340 let mut attribute = super::PROPERTY_ATTRIBUTE_DEFAULT;
1341 if item.writable {
1342 attribute |= super::PROPERTY_ATTRIBUTE_WRITABLE;
1343 }
1344 if item.enumerable {
1345 attribute |= super::PROPERTY_ATTRIBUTE_ENUMERABLE;
1346 }
1347 if item.configurable {
1348 attribute |= super::PROPERTY_ATTRIBUTE_CONFIGURABLE;
1349 }
1350
1351 let prop = props.entry(&item.js_name).or_insert_with(|| {
1352 quote! {
1353 napi::bindgen_prelude::Property::new().with_utf8_name(#js_name).unwrap().with_property_attributes(napi::bindgen_prelude::PropertyAttributes::from_bits(#attribute).unwrap())
1354 }
1355 });
1356
1357 let appendix = match item.kind {
1358 FnKind::Constructor => quote! { .with_ctor(#intermediate_name) },
1359 FnKind::Getter => quote! { .with_getter(#intermediate_name) },
1360 FnKind::Setter => quote! { .with_setter(#intermediate_name) },
1361 _ => {
1362 if item.fn_self.is_some() {
1363 quote! { .with_method(#intermediate_name) }
1364 } else {
1365 quote! { .with_method(#intermediate_name).with_property_attributes(napi::bindgen_prelude::PropertyAttributes::Static) }
1366 }
1367 }
1368 };
1369
1370 appendix.to_tokens(prop);
1371 }
1372
1373 let mut props: Vec<_> = props.into_iter().collect();
1374 props.sort_by_key(|(_, prop)| prop.to_string());
1375 let props = props.into_iter().map(|(_, prop)| prop);
1376 let props_wasm = props.clone();
1377 let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
1378 Ok(quote! {
1379 #[allow(non_snake_case)]
1380 #[allow(clippy::all)]
1381 mod #mod_name {
1382 use super::*;
1383 #(#methods)*
1384
1385 #[cfg(all(not(test), not(target_family = "wasm")))]
1386 #[napi::ctor::ctor(crate_path=napi::ctor)]
1387 fn #register_name() {
1388 napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props),*]);
1389 }
1390
1391 #[cfg(all(not(test), target_family = "wasm"))]
1392 #[no_mangle]
1393 extern "C" fn #register_name() {
1394 napi::__private::register_class(std::any::TypeId::of::<#name>(), #js_mod_ident, #js_name, vec![#(#props_wasm),*]);
1395 }
1396 }
1397 })
1398 }
1399}
1400
1401pub fn rm_raw_prefix(s: &str) -> &str {
1402 if let Some(stripped) = s.strip_prefix("r#") {
1403 stripped
1404 } else {
1405 s
1406 }
1407}
1408
1409fn remove_lifetime_in_type(ty: &mut syn::Type) {
1410 if let syn::Type::Path(syn::TypePath { path, .. }) = ty {
1411 path.segments.iter_mut().for_each(|segment| {
1412 if let syn::PathArguments::AngleBracketed(ref mut args) = segment.arguments {
1413 args.args.iter_mut().for_each(|arg| match arg {
1414 syn::GenericArgument::Type(ref mut ty) => {
1415 remove_lifetime_in_type(ty);
1416 }
1417 syn::GenericArgument::Lifetime(lifetime) => {
1418 lifetime.ident = Ident::new("_", lifetime.ident.span());
1419 }
1420 _ => {}
1421 });
1422 }
1423 });
1424 }
1425}