derive_environment_macros/
lib.rs1#![doc = include_str!("../README.md")]
2
3use convert_case::{Case, Casing};
4use darling::{ast, FromDeriveInput, FromField};
5use proc_macro2::TokenStream;
6use quote::quote;
7use syn::*;
8
9#[derive(Debug, FromDeriveInput)]
10#[darling(attributes(env), supports(struct_named))]
11#[allow(dead_code)]
12struct EnvArgs {
13 ident: syn::Ident,
14 generics: syn::Generics,
15 data: ast::Data<(), EnvFieldArgs>,
16}
17
18#[derive(Debug, FromField)]
19#[darling(attributes(env))]
20#[allow(dead_code)]
21struct EnvFieldArgs {
22 ident: Option<syn::Ident>,
23 ty: syn::Type,
24
25 #[darling(default)]
26 ignore: bool,
27}
28
29#[proc_macro_derive(FromEnv, attributes(prefix, env))]
34pub fn environment(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
35 let input = parse_macro_input!(input as DeriveInput);
37
38 let args = match EnvArgs::from_derive_input(&input) {
39 Ok(v) => v,
40 Err(e) => {
41 return e.write_errors().into();
42 }
43 };
44
45 let name = input.ident;
46 let fields = args.data.as_ref().take_struct().unwrap().fields;
47 let parseable_fields = env_from_parseable(&fields);
48
49 let expanded = quote! {
51 impl ::derive_environment::FromEnv for #name {
52 fn with_env(&mut self, prefix: &str) -> ::derive_environment::Result<bool> {
53 let mut found_match = false;
56 #parseable_fields
57 ::derive_environment::Result::Ok(found_match)
58 }
59 }
60 };
61
62 expanded.into()
64}
65
66fn to_variable(field: &&EnvFieldArgs) -> String {
67 field
68 .ident
69 .clone()
70 .unwrap()
71 .to_string()
72 .to_case(Case::UpperSnake)
73}
74
75fn to_field(field: &&EnvFieldArgs) -> Ident {
76 field.ident.clone().unwrap()
77}
78
79fn env_from_parseable(fields: &[&EnvFieldArgs]) -> TokenStream {
80 let mut tokens = TokenStream::new();
81
82 for field in fields.iter().filter(|x| !x.ignore) {
83 let f = to_field(field);
84 let var = to_variable(field);
85
86 tokens.extend(quote! {
87 let name = ::std::format!("{prefix}_{}", #var);
88
89 if derive_environment::FromEnv::with_env(&mut self.#f, &name)? {
90 found_match = true;
91 }
92 });
93 }
94
95 tokens
96}