rust_decouple_derive/
lib.rs1use quote::quote;
2use syn::{parse_macro_input, DeriveInput, GenericArgument, Ident, PathArguments, Type};
3
4#[proc_macro_derive(Decouple)]
5pub fn derive_env_var_parser(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
6 let input = parse_macro_input!(input as DeriveInput);
7 let struct_name = &input.ident;
8
9 let expanded = match input.data {
10 syn::Data::Struct(ref data_struct) => {
11 let fields: Vec<_> = data_struct
12 .fields
13 .iter()
14 .filter_map(|f| {
15 let ident = f.clone().ident;
16 let ty = f.ty.clone();
17 let ty_is_vec = f.ty.clone();
18 if ident.as_ref().is_some() {
19 Some((ident.unwrap(), ty, is_vec(ty_is_vec)))
20 } else {
21 None
22 }
23 })
24 .collect();
25 let non_vec_fields: Vec<_> = fields.iter().filter(|(_, _, v)| !v).collect();
26 let non_vec_fields = gen_fields(non_vec_fields);
27 let vec_fields: Vec<_> = fields.iter().filter(|(_, _, v)| *v).collect();
28 let vec_fields = gen_fields(vec_fields);
29
30 quote! {
31 impl Decouple for #struct_name {
32 type Error = rust_decouple::core::FromEnvironmentError;
33 fn parse() -> Result<Self, Self::Error> where Self: Sized {
34 Ok(Self {
35 #non_vec_fields
36 #vec_fields
37 })
38 }
39 }
40 }
41 }
42 _ => panic!("#[derive(Decouple)] is only defined for structs"),
43 };
44
45 expanded.into()
46}
47
48fn is_vec(v: Type) -> bool {
50 let v = quote!(#v).to_string();
51 v.starts_with("Vec")
52}
53
54fn gen_fields(fields: Vec<&(Ident, Type, bool)>) -> proc_macro2::TokenStream {
55 if fields.len() == 0 {
56 return quote! {};
57 }
58
59 let field_names: Vec<_> = fields.iter().map(|(f, _, _)| f).collect();
60 let field_names_uppercase: Vec<_> = fields
61 .iter()
62 .map(|(f, _, _)| f.to_string().to_uppercase())
63 .collect();
64 let field_types: Vec<_> = fields.iter().map(|(_, t, _)| t).collect();
65 let is_vec = fields.iter().next().unwrap().2;
66
67 if is_vec {
68 let extracted_field_types: Vec<_> = field_types
69 .iter()
70 .map(|ty| extract_inner_type_from_vec(ty).unwrap())
71 .collect();
72
73 quote! {
74 #(#field_names: (rust_decouple::core::VecEnvironment::from::<#extracted_field_types>(#field_names_uppercase, None))?,)*
75 }
76 } else {
77 quote! {
78 #(#field_names: (rust_decouple::core::Environment::from::<#field_types>(#field_names_uppercase, None))?,)*
79 }
80 }
81}
82
83fn extract_inner_type_from_vec(ty: &Type) -> Option<&Type> {
85 if let Type::Path(type_path) = ty {
87 if let Some(path_segment) = type_path.path.segments.last() {
89 if path_segment.ident == "Vec" {
90 if let PathArguments::AngleBracketed(angle_bracketed_args) = &path_segment.arguments
92 {
93 if let Some(GenericArgument::Type(inner_type)) =
95 angle_bracketed_args.args.first()
96 {
97 return Some(inner_type); }
99 }
100 }
101 }
102 }
103 None
104}