1use proc_macro::TokenStream;
2use proc_macro2::Ident;
3use quote::{format_ident, quote};
4use syn::{parse_macro_input, Data, DeriveInput, Fields};
5
6#[proc_macro_derive(EnvMap)]
7pub fn env_map_derive(input: TokenStream) -> TokenStream {
8 let ast = parse_macro_input!(input as DeriveInput);
9
10 let main_struct = ast.ident;
11 let const_value = Ident::new(
12 &format!("{}", &main_struct).to_uppercase(),
13 main_struct.span(),
14 );
15 let builder = format_ident!("{}Builder", &main_struct);
16 let fields = if let Data::Struct(data_struct) = &ast.data {
17 match &data_struct.fields {
18 Fields::Named(fields) => &fields.named,
19 Fields::Unnamed(fields) => &fields.unnamed,
20 Fields::Unit => panic!("Unit structs are not supported"),
21 }
22 } else {
23 panic!("ReplicateStruct is only implemented for structs");
24 };
25 let fields_clone = fields.clone();
26 let fields_iter = fields_clone.iter().filter_map(|f| f.ident.as_ref());
27 let fields_into_iter = fields.iter();
28 let gen = quote! {
29 use serde::Deserialize;
30 use std::{env, sync::OnceLock};
31
32 #[derive(Deserialize, Clone)]
33 struct #builder {
34 #(#fields_into_iter),*
35 }
36
37 impl Default for #builder {
38 fn default() -> Self {
39 let dotenv = dotenvy::dotenv();
40 match envy::from_env::<#builder>() {
41 Ok(builder) => builder,
42 Err(_) => panic!("missing env vars"),
43 }
44 }
45 }
46
47 impl #builder {
48 pub fn build(self) -> #main_struct {
49 #main_struct {
50 #(#fields_iter: self.#fields_iter.clone(),)*
51 }
52 }
53 }
54
55 impl Default for #main_struct {
56 fn default() -> Self {
57 let builder = #builder::default();
58 builder.build()
59 }
60 }
61
62 impl #main_struct {
63 pub fn get_config() -> OnceLock<#main_struct> {
64 #const_value
66 }
67 }
68
69 const #const_value: OnceLock<#main_struct> = OnceLock::new();
70 };
71 gen.into()
72}