native_proc/
lib.rs

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/**
20 * Create conversions between generated types for struct types
21 *  From <NapiType> for <TonicType>
22 *  From <TonicType> for <NapiType>
23 */
24
25#[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                // Assume args.args.len() == 1 - proto types aren't more complex
55                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/**
276 * Create conversions between numeric simple enum types and their String names
277 *  fn <NapiType>_to_str(val: <NapiType>)
278 *  fn <NapiType>_from_str(val: String)
279 */
280
281#[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  // Get enum variants
293  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  // Map the variants into 'fields'.
300  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  // Map variants to match statements per value
322  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}