1use proc_macro::{TokenStream, TokenTree};
2use quote::{quote};
3use syn::{parse_macro_input, DeriveInput, Data, DataEnum, Ident, Variant, Fields};
4
5static BASE_TYPES: &'static [&str] = &[
6 "i8", "i16", "i32", "i64", "isize", "u8", "u16", "u32", "usize", "f32",
7 "bool", "String", "&str", "char",
8];
9enum ConvertType {
10 As,
11 BigInt,
12}
13static CONVERT_TYPES: &'static [(&str, ConvertType)] = &[
14 ("f64", ConvertType::As),
15 ("f32", ConvertType::As),
16 ("BigInt", ConvertType::BigInt),
17];
18
19#[proc_macro_derive(FromToNapi, attributes(module_path_str, proto_oneof, native_proc))]
26pub fn from_type_derive(input: TokenStream) -> TokenStream {
27 let ast = parse_macro_input!(input as DeriveInput);
28 let napi_type = &ast.ident;
29 let napi_str = napi_type.to_string();
30 let last_split = &napi_str.split("_").last().expect("Type cannot be found");
31 let original_type = &syn::Ident::new(
32 last_split,
33 ast.ident.span()
34 );
35
36 let mut fields_from: Vec<quote::__private::TokenStream> = Vec::new();
37 let mut fields_to: Vec<quote::__private::TokenStream> = Vec::new();
38 let mut types: Vec<quote::__private::TokenStream> = Vec::new();
39 match ast.data {
40 syn::Data::Struct(struct_data) => {
41 for field in struct_data.fields {
42 let field_ident = field.ident.expect("No ident in field");
43
44 match field.ty {
45 syn::Type::Path(type_path) => {
46 let last_segment = type_path.path.segments.last().expect("Failed to get last segment");
47 let last_segment_name = last_segment.ident.to_string();
48 let last_segment_str = last_segment_name.as_str();
49
50 let mut is_base_type = false;
51
52 match &last_segment.arguments {
53 syn::PathArguments::AngleBracketed(bracketed) => {
54 match &bracketed.args[0] {
56 syn::GenericArgument::Type(generic_type) => {
57 match generic_type {
58 syn::Type::Path(path_type) => {
59 let final_name = path_type.path.segments.last().expect("Failed to get last segment").ident.to_string();
60
61 if BASE_TYPES.iter().any(|&i| i == final_name) {
62 is_base_type = true;
63 }
64 }
65 _ => todo!("Only implemented Type::Path")
66 }
67 }
68 _ => todo!("Only implemented GenericArgument::Type")
69 }
70 }
71 _ => { }
72 }
73
74 let value_assign_from = quote! { value.#field_ident.into() };
75 let mut value_assign_to = quote! { value.#field_ident.into() };
76 let convert_type = CONVERT_TYPES.iter().find(|&i| i.0 == last_segment_str);
77 if let Some(val) = convert_type {
78 let type_stream = get_original_type_attr(&field.attrs);
79 match val.1 {
80 ConvertType::As => {
81 value_assign_to = quote! { (value.#field_ident as #type_stream).into() };
82 }
83 ConvertType::BigInt => {
84 value_assign_to = quote! { (value.#field_ident.get_i128().1 as #type_stream).into() };
85 }
86 }
87 }
88
89 let base_field_from = quote! {
90 #field_ident: #value_assign_from,
91 };
92 let base_field_to = quote! {
93 #field_ident: #value_assign_to,
94 };
95
96 if !is_base_type {
97 match last_segment_str {
98 "Option" => {
99 let convert = quote! {
100 #field_ident: prost_types::convert_option(value.#field_ident),
101 };
102 fields_from.push(convert.clone());
103 fields_to.push(convert);
104 }
105 "Vec" => {
106 let convert = quote! {
107 #field_ident: prost_types::convert_vec(value.#field_ident),
108 };
109 fields_from.push(convert.clone());
110 fields_to.push(convert);
111 }
112 _ => {
113 fields_from.push(base_field_from);
114 fields_to.push(base_field_to);
115 }
116 }
117 } else {
118 fields_from.push(base_field_from);
119 fields_to.push(base_field_to);
120 }
121 types.push(quote! {#field_ident});
122 }
123 _ => todo!("Field type can only be of Type::Path")
124 }
125 }
126 }
127 _ => panic!("FromToNapi can only be applied to Struct types")
128 }
129
130 let is_proto_oneof = get_oneof_attr(&ast.attrs);
131
132 let expanded_struct_from = if is_proto_oneof {
133 impl_enum_from_to(&ast.attrs, original_type, napi_type, types)
134 } else {
135 impl_struct_from_to(&ast.attrs, original_type, napi_type, fields_from, fields_to)
136 };
137
138 expanded_struct_from
139}
140
141fn impl_struct_from_to(attrs: &Vec<syn::Attribute>, original_type: &syn::Ident, napi_type: &syn::Ident,
142 fields_from: Vec<quote::__private::TokenStream>, fields_to: Vec<quote::__private::TokenStream>) -> TokenStream {
143 let module_str = get_module_attr(attrs);
144 let original_path = format!("{}{}", module_str, quote!(#original_type));
145 let original_type: quote::__private::TokenStream = original_path.parse().unwrap();
146
147 let expanded = quote! {
148 impl From<#original_type> for #napi_type {
149 fn from(value: #original_type) -> #napi_type {
150 #napi_type {
151 #(#fields_from)*
152 }
153 }
154 }
155 impl From<#napi_type> for #original_type {
156 fn from(value: #napi_type) -> #original_type {
157 #original_type {
158 #(#fields_to)*
159 }
160 }
161 }
162 };
163
164 TokenStream::from(expanded)
165}
166
167fn impl_enum_from_to(attrs: &Vec<syn::Attribute>, original_type: &syn::Ident, napi_type: &syn::Ident, types: Vec<quote::__private::TokenStream>) -> TokenStream {
168 let module_str = get_module_attr(attrs);
169 let original_path = format!("{}{}", module_str, quote!(#original_type));
170 let original_type: quote::__private::TokenStream = original_path.parse().unwrap();
171
172 let expanded = quote! {
173 impl From<#original_type> for #napi_type {
174 fn from(value: #original_type) -> Self {
175 match value {
176 #(#original_type::#types(value) => {
177 let mut ret = #napi_type::default();
178 ret.#types = Some(value.into());
179 ret
180 })*
181 }
182 }
183 }
184 impl From<#napi_type> for #original_type {
185 fn from(value: #napi_type) -> Self {
186 #(if value.#types.is_some() {
187 return #original_type::#types(value.#types.expect("???").into());
188 })*
189 return #napi_type::default().into();
190 }
191 }
192 };
193
194 TokenStream::from(expanded)
195}
196
197fn get_oneof_attr(attrs: &Vec<syn::Attribute>) -> bool {
198 for attr in attrs.iter() {
199 match &attr.meta {
200 syn::Meta::NameValue(name) => {
201 for segment in name.path.segments.iter() {
202 if segment.ident == "proto_oneof" {
203 match &name.value {
204 syn::Expr::Lit(value) => {
205 match &value.lit {
206 syn::Lit::Bool(bool_val) => {
207 return bool_val.value();
208 }
209 _ => panic!("Expected a bool in proto_oneof value")
210 }
211 }
212 _ => panic!("Expected a literal expression in proto_oneof")
213 }
214 }
215 }
216 }
217 _ => { }
218 }
219 }
220 false
221}
222
223fn get_module_attr(attrs: &Vec<syn::Attribute>) -> String {
224 for attr in attrs.iter() {
225 match &attr.meta {
226 syn::Meta::NameValue(name) => {
227 for segment in name.path.segments.iter() {
228 if segment.ident == "module_path_str" {
229 match &name.value {
230 syn::Expr::Lit(value) => {
231 match &value.lit {
232 syn::Lit::Str(string_val) => {
233 let token = format!("crate::{}", string_val.value());
234 return token;
235 }
236 _ => panic!("Expected a str in module_path_str value")
237 }
238 }
239 _ => panic!("Expected a literal expression in module_path_str")
240 }
241 }
242 }
243 }
244 _ => { }
245 }
246 }
247 return "crate::".to_string();
248}
249
250fn get_original_type_attr(attrs: &Vec<syn::Attribute>) -> quote::__private::TokenStream {
251 for attr in attrs.iter() {
252 match &attr.meta {
253 syn::Meta::List(list) => {
254 let mut is_field_og = false;
255 let tokenstream = TokenStream::from(list.tokens.clone());
256 for token in tokenstream {
257 match token {
258 TokenTree::Ident(ident) => {
259 if is_field_og {
260 return ident.to_string().parse().unwrap();
261 } else if ident.to_string() == "field_og" {
262 is_field_og = true;
263 }
264 }
265 _ => { }
266 }
267 }
268 }
269 _ => { }
270 }
271 }
272 return "".parse().unwrap();
273}
274
275#[proc_macro_derive(EnumConversions, attributes(module_path_str))]
282pub fn enum_conversion(input: TokenStream) -> TokenStream {
283 let ast = parse_macro_input!(input as DeriveInput);
284 let napi_type = &ast.ident;
285 let napi_str = napi_type.to_string();
286 let last_split = &napi_str.split("_").last().expect("Type cannot be found");
287 let original_type = &syn::Ident::new(
288 last_split,
289 ast.ident.span()
290 );
291
292 let punctuated_variants = match ast.data {
294 Data::Enum(DataEnum { variants, .. }) => variants,
295 Data::Struct(_) => panic!("Enumeration can not be derived for a struct"),
296 Data::Union(..) => panic!("Enumeration can not be derived for a union"),
297 };
298
299 let mut variants: Vec<(Ident, i32)> = Vec::new();
301 for (i, Variant {
302 ident,
303 fields,
304 ..
305 }) in punctuated_variants.iter().enumerate()
306 {
307 match fields {
308 Fields::Unit => (),
309 Fields::Named(_) | Fields::Unnamed(_) => {
310 panic!("Enumeration variants may not have fields")
311 }
312 }
313
314 variants.push((ident.clone(), i as i32))
315 }
316
317 if variants.is_empty() {
318 panic!("Enumeration must have at least one variant");
319 }
320
321 let from = variants.iter().map(
323 |&(ref variant, ref value)| quote!(#value => ::core::option::Option::Some(#napi_type::#variant)),
324 );
325
326 let module_str = get_module_attr(&ast.attrs);
327 let original_path = format!("{}{}", module_str, quote!(#original_type));
328 let original_type: quote::__private::TokenStream = original_path.parse().unwrap();
329
330 let to_str = &syn::Ident::new(&format!("{}_to_str", quote!(#napi_type)), ast.ident.span());
331 let from_str = &syn::Ident::new(&format!("{}_from_str", quote!(#napi_type)), ast.ident.span());
332
333 let expanded = quote! {
334 impl #napi_type {
335 fn from_i32(val: i32) -> std::option::Option<Self> {
336 match val {
337 #(#from,)*
338 _ => None,
339 }
340 }
341 }
342
343 #[napi_derive::napi]
344 pub fn #to_str(val: #napi_type) -> String {
345 match #original_type::from_i32(val as i32) {
346 None => "UNKNOWN".to_string(),
347 Some(og_val) => og_val.as_str_name().to_string(),
348 }
349 }
350
351 #[napi_derive::napi]
352 pub fn #from_str(val: String) -> std::option::Option<#napi_type> {
353 match #original_type::from_str_name(val.as_str()) {
354 None => None,
355 Some(og_val) => #napi_type::from_i32(og_val as i32),
356 }
357 }
358 };
359
360 TokenStream::from(expanded)
361}