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}