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