from_env_derive/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::{Ident, Literal, TokenStream as TokenStream2};
3use syn::parse_macro_input;
4
5#[derive(Default)]
6struct Tags {
7    env_var: Option<Ident>,
8    env_default: Option<Literal>,
9}
10
11fn init_value(field: &syn::Field) -> TokenStream2 {
12    let mut tags = Tags::default();
13    for attr in &field.attrs {
14        match attr.path() {
15            p if p.is_ident("env_var") => tags.env_var = Some(attr.parse_args().unwrap()),
16            p if p.is_ident("env_default") => tags.env_default = Some(attr.parse_args().unwrap()),
17            _ => panic!("Unknown attribute"),
18        }
19    }
20    let name = field.ident.as_ref().unwrap();
21    let typ = &field.ty;
22    if let Some(env_var) = tags.env_var {
23        if let Some(env_default) = tags.env_default {
24            return quote::quote! {
25                #name: <#typ as FromEnv>::from_env(&stringify!(#env_var), std::env::var(&stringify!(#env_var)).ok(), Some(#env_default.to_owned())),
26            };
27        }
28        return quote::quote! {
29            #name: <#typ as FromEnv>::from_env(&stringify!(#env_var), std::env::var(&stringify!(#env_var)).ok(), None),
30        };
31    }
32    if let Some(env_default) = tags.env_default {
33        return quote::quote! {
34            #name: <#typ as FromEnv>::from_env(&stringify!(#name), std::env::var(&stringify!(#name).to_uppercase()).ok(), Some(#env_default.to_owned())),
35        };
36    }
37    quote::quote! {
38        #name: <#typ as FromEnv>::from_env(&stringify!(#name), std::env::var(&stringify!(#name).to_uppercase()).ok(), None),
39    }
40}
41
42#[proc_macro_derive(FromEnvDerive, attributes(env_var, env_default))]
43pub fn from_env(input: TokenStream) -> TokenStream {
44    let input = parse_macro_input!(input as syn::DeriveInput);
45    let name = input.ident;
46    if let syn::Data::Struct(data) = input.data {
47        let mut values = Vec::new();
48        for field in data.fields {
49            values.push(init_value(&field));
50        }
51        return quote::quote! {
52            impl #name {
53                pub fn from_env() -> Self {
54                    Self {
55                        #(#values)*
56                    }
57                }
58            }
59        }
60        .into();
61    }
62    panic!("Only structs are supported");
63}