locustdb_derive/
enum_syntax.rs1use super::proc_macro::TokenStream;
2use syn::*;
3use proc_macro2::Span;
4
5pub fn enum_syntax(input: TokenStream) -> TokenStream {
6 let input = parse_macro_input!(input as DeriveInput);
8
9
10 if let Data::Enum(DataEnum { variants, .. }) = input.data {
11 let enum_ident = input.ident;
12 let mut constructors = Vec::<Item>::new();
13 let boxed: Type = parse_quote!(Box<#enum_ident>);
14 let string: Type = parse_quote!(String);
15 for variant in variants.into_iter() {
16 if let Fields::Named(fields) = variant.fields {
17 let variant_ident = variant.ident.clone();
18 let ident_snake = studley_to_snake(variant.ident);
19 let mut fn_inputs = Vec::<FnArg>::new();
20 let mut struct_args = Vec::<FieldValue>::new();
21 let mut fn_generics = Vec::<GenericArgument>::new();
22 for field in fields.named.into_iter() {
23 let field_ident = field.ident.clone().unwrap();
24 if field.ty == boxed {
25 let type_ident = Ident::new(&format!("_T{}", fn_generics.len()), Span::call_site());
26 fn_generics.push(parse_quote!(#type_ident: Into<#enum_ident>));
27 fn_inputs.push(parse_quote!(#field_ident: #type_ident));
28 struct_args.push(parse_quote!(#field_ident: Box::new(#field_ident.into())));
29 } else if field.ty == string {
30 fn_inputs.push(parse_quote!(#field_ident: &str));
31 struct_args.push(parse_quote!(#field_ident: #field_ident.to_string()));
32 } else {
33 fn_inputs.push(parse_quote!(#field));
34 struct_args.push(parse_quote!(#field_ident));
35 }
36 }
37
38 let item = parse_quote! {
39 pub fn #ident_snake <#(#fn_generics),*> ( #(#fn_inputs),* ) -> #enum_ident {
40 #enum_ident::#variant_ident { #(#struct_args),* }
41 }
42 };
43 constructors.push(item);
44 }
45 }
46 let expanded = quote! {
47 pub mod syntax {
48 use super::*;
49
50 #(#constructors)*
51 }
52 };
53
54 TokenStream::from(expanded)
56 } else {
57 Span::call_site().unstable().error(format!("EnumSyntax must be applied to an enum"));
58 TokenStream::from(quote!())
59 }
60}
61
62fn studley_to_snake(ident: Ident) -> Ident {
63 let mut snake_case = String::new();
64 let mut previous_lowercase = false;
65 for c in format!("{}", ident).chars() {
66 if c.is_uppercase() {
67 if previous_lowercase {
68 snake_case.push('_');
69 }
70 previous_lowercase = false;
71 for l in c.to_lowercase() {
72 snake_case.push(l);
73 }
74 } else {
75 previous_lowercase = true;
76 snake_case.push(c);
77 }
78 }
79 Ident::new(&snake_case, ident.span())
80}