1use proc_macro2::{Ident, Literal, Span, TokenStream};
2use quote::ToTokens;
3
4use crate::{codegen::js_mod_to_token_stream, BindgenResult, NapiEnum, TryToTokens};
5
6impl TryToTokens for NapiEnum {
7 fn try_to_tokens(&self, tokens: &mut TokenStream) -> BindgenResult<()> {
8 let register: TokenStream = self.gen_module_register();
9 let napi_value_conversion: TokenStream = self.gen_napi_value_map_impl();
10
11 (quote! {
12 #napi_value_conversion
13 #register
14 })
15 .to_tokens(tokens);
16
17 Ok(())
18 }
19}
20
21impl NapiEnum {
22 fn gen_napi_value_map_impl(&self) -> TokenStream {
23 let name = &self.name;
24 let name_str = self.name.to_string();
25 let mut from_napi_branches = vec![];
26 let mut to_napi_branches = vec![];
27
28 self.variants.iter().for_each(|v| {
29 let val: Literal = (&v.val).into();
30 let v_name = &v.name;
31
32 from_napi_branches.push(quote! { #val => Ok(#name::#v_name) });
33 to_napi_branches.push(quote! { #name::#v_name => #val });
34 });
35
36 quote! {
37 impl napi::bindgen_prelude::TypeName for #name {
38 fn type_name() -> &'static str {
39 #name_str
40 }
41
42 fn value_type() -> napi::ValueType {
43 napi::ValueType::Object
44 }
45 }
46
47 impl napi::bindgen_prelude::ValidateNapiValue for #name {
48 unsafe fn validate(
49 env: napi::bindgen_prelude::sys::napi_env,
50 napi_val: napi::bindgen_prelude::sys::napi_value
51 ) -> napi::bindgen_prelude::Result<napi::sys::napi_value> {
52 napi::bindgen_prelude::assert_type_of!(env, napi_val, napi::bindgen_prelude::ValueType::Number)?;
53 Ok(std::ptr::null_mut())
54 }
55 }
56
57 impl napi::bindgen_prelude::FromNapiValue for #name {
58 unsafe fn from_napi_value(
59 env: napi::bindgen_prelude::sys::napi_env,
60 napi_val: napi::bindgen_prelude::sys::napi_value
61 ) -> napi::bindgen_prelude::Result<Self> {
62 let val = napi::bindgen_prelude::FromNapiValue::from_napi_value(env, napi_val).map_err(|e| {
63 napi::bindgen_prelude::error!(
64 e.status,
65 "Failed to convert napi value into enum `{}`. {}",
66 #name_str,
67 e,
68 )
69 })?;
70
71 match val {
72 #(#from_napi_branches,)*
73 _ => {
74 Err(napi::bindgen_prelude::error!(
75 napi::bindgen_prelude::Status::InvalidArg,
76 "value `{:?}` does not match any variant of enum `{}`",
77 val,
78 #name_str
79 ))
80 }
81 }
82 }
83 }
84
85 impl napi::bindgen_prelude::ToNapiValue for #name {
86 unsafe fn to_napi_value(
87 env: napi::bindgen_prelude::sys::napi_env,
88 val: Self
89 ) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
90 let val = match val {
91 #(#to_napi_branches,)*
92 };
93
94 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, val)
95 }
96 }
97 }
98 }
99
100 fn gen_module_register(&self) -> TokenStream {
101 let name_str = self.name.to_string();
102 let js_name_lit = Literal::string(&format!("{}\0", &self.js_name));
103 let register_name = &self.register_name;
104
105 let mut define_properties = vec![];
106
107 for variant in self.variants.iter() {
108 let name_lit = Literal::string(&format!("{}\0", variant.name));
109 let val_lit: Literal = (&variant.val).into();
110
111 define_properties.push(quote! {
112 {
113 let name = std::ffi::CStr::from_bytes_with_nul_unchecked(#name_lit.as_bytes());
114 napi::bindgen_prelude::check_status!(
115 napi::bindgen_prelude::sys::napi_set_named_property(
116 env,
117 obj_ptr, name.as_ptr(),
118 napi::bindgen_prelude::ToNapiValue::to_napi_value(env, #val_lit)?
119 ),
120 "Failed to defined enum `{}`",
121 #js_name_lit
122 )?;
123 };
124 })
125 }
126
127 let callback_name = Ident::new(
128 &format!("__register__enum__{}_callback__", name_str),
129 Span::call_site(),
130 );
131
132 let js_mod_ident = js_mod_to_token_stream(self.js_mod.as_ref());
133
134 quote! {
135 #[allow(non_snake_case)]
136 #[allow(clippy::all)]
137 unsafe fn #callback_name(env: napi::bindgen_prelude::sys::napi_env) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
138 use std::ffi::CString;
139 use std::ptr;
140
141 let mut obj_ptr = ptr::null_mut();
142
143 napi::bindgen_prelude::check_status!(
144 napi::bindgen_prelude::sys::napi_create_object(env, &mut obj_ptr),
145 "Failed to create napi object"
146 )?;
147
148 #(#define_properties)*
149
150 Ok(obj_ptr)
151 }
152 #[allow(non_snake_case)]
153 #[allow(clippy::all)]
154 #[cfg(all(not(test), not(feature = "noop"), not(target_family = "wasm")))]
155 #[napi::bindgen_prelude::ctor]
156 fn #register_name() {
157 napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #callback_name);
158 }
159 #[allow(non_snake_case)]
160 #[allow(clippy::all)]
161 #[cfg(all(not(test), not(feature = "noop"), target_family = "wasm"))]
162 #[no_mangle]
163 extern "C" fn #register_name() {
164 napi::bindgen_prelude::register_module_export(#js_mod_ident, #js_name_lit, #callback_name);
165 }
166 }
167 }
168}
169