env_settings_derive/
lib.rs1#![deny(missing_docs)]
2#![doc(
3 html_logo_url = "https://raw.githubusercontent.com/dariocurr/env-settings/main/docs/logo.svg",
4 html_favicon_url = "https://raw.githubusercontent.com/dariocurr/env-settings/main/docs/logo.ico"
5)]
6
7use proc_macro::TokenStream;
10use quote::quote;
11use std::collections::HashMap;
12use syn::parse;
13
14mod utils;
15
16#[proc_macro_derive(EnvSettings, attributes(env_settings))]
18pub fn env_settings_derive(input: TokenStream) -> TokenStream {
19 let input = parse(input).unwrap();
21
22 implement(&input)
24}
25
26fn implement(input: &utils::input::EnvSettingsInput) -> TokenStream {
28 let struct_name = &input.name;
29
30 let mut new_args = Vec::new();
31 let mut new_impls = Vec::new();
32 let mut from_env_impls = Vec::new();
33 let mut from_env_args = Vec::new();
34
35 let mut env_variables_impls = quote! {};
36 let mut file_path_impls = quote! {};
37
38 if let Some(file_path) = &input.params.file_path {
39 if input.params.delay {
40 file_path_impls = quote! { env_settings_utils::load_env_file_path(#file_path)?; }
41 } else {
42 env_settings_utils::load_env_file_path(file_path).unwrap();
43 }
44 }
45
46 let case_insensitive = input.params.case_insensitive;
47
48 let env_variables = if input.params.delay {
49 env_variables_impls = quote! {
50 let env_variables = env_settings_utils::get_env_variables(#case_insensitive);
51 };
52 HashMap::new()
53 } else {
54 env_settings_utils::get_env_variables(case_insensitive)
55 };
56
57 let prefix = input.params.prefix.clone().unwrap_or_default();
58
59 for field in &input.fields {
60 match field {
61 utils::field::EnvSettingsField::NonParsable(non_parsable_field) => {
62 let name = &non_parsable_field.name;
63 let type_ = &non_parsable_field.type_;
64 let argument = quote! { #name: #type_ };
65 new_args.push(argument.clone());
66 from_env_args.push(argument);
67 let value = quote! {#name};
68 new_impls.push(value.clone());
69 from_env_impls.push(value);
70 }
71 utils::field::EnvSettingsField::Parsable(parsable_field) => {
72 let name = &parsable_field.name;
73 let name_label = &parsable_field.name_label;
74 let type_ = &parsable_field.type_;
75 let type_label = &parsable_field.type_label;
76 let optional_type = &parsable_field.optional_type;
77
78 let mut env_variable = parsable_field
79 .variable
80 .to_owned()
81 .unwrap_or(format!("{prefix}{name}"));
82 if case_insensitive {
83 env_variable = env_variable.to_lowercase();
84 }
85
86 let (optional_value_impl, default_value_impl, new_arg_impl, parse_type) =
88 match optional_type {
89 Some(optional_type) => (
90 quote! { Some(value) },
91 quote! { None },
92 quote! { #name: #type_ },
93 optional_type,
94 ),
95 None => (
96 quote! { value },
97 quote! { return Err(env_settings_utils::EnvSettingsError::NotExists(#env_variable)) },
98 quote! { #name: Option<#type_> },
99 type_,
100 ),
101 };
102
103 let convert_err_impl = quote! {
105 return Err(env_settings_utils::EnvSettingsError::Convert(
106 #name_label,
107 value_to_parse.to_owned(),
108 #type_label,
109 ))
110 };
111 let default_impl = match &parsable_field.default {
112 Some(value_to_parse) => {
113 quote! {
114 match #value_to_parse.parse::<#parse_type>() {
115 Ok(value) => #optional_value_impl,
116 Err(_) => {
117 let value_to_parse = #value_to_parse;
118 #convert_err_impl
119 }
120 }
121 }
122 }
123 None => default_value_impl,
124 };
125
126 let parse_impl = quote! {
128 match value_to_parse.parse::<#parse_type>() {
129 Ok(value) => #optional_value_impl,
130 Err(_) => #convert_err_impl
131 }
132 };
133
134 let env_value_impl = if input.params.delay {
136 quote! {
137 match env_variables.get(#env_variable) {
138 Some(value_to_parse) => {#parse_impl},
139 None => #default_impl,
140 }
141 }
142 } else {
143 match env_variables.get(&env_variable) {
144 Some(value_to_parse) => quote! {
145 let value_to_parse = #value_to_parse;
146 #parse_impl
147 },
148 None => default_impl,
149 }
150 };
151
152 new_impls.push(quote! {
153 #name: match #name {
154 Some(value) => #optional_value_impl,
155 None => #env_value_impl
156 }
157 });
158 new_args.push(new_arg_impl);
159 from_env_impls.push(quote! { #name: #env_value_impl });
160 }
161 }
162 }
163
164 let pre_impls = quote! {
165 #file_path_impls
166 #env_variables_impls
167 };
168
169 let generated_impl = quote! {
170
171 impl #struct_name {
172
173 #[allow(clippy::too_many_arguments)]
176 pub fn new(#(#new_args),*) -> env_settings_utils::EnvSettingsResult<Self> {
177 #pre_impls
178 let instance = Self {
179 #(#new_impls),*
180 };
181 Ok(instance)
182 }
183
184 #[allow(clippy::too_many_arguments)]
190 pub fn from_env(#(#from_env_args),*) -> env_settings_utils::EnvSettingsResult<Self> {
191 #pre_impls
192 let instance = Self {
193 #(#from_env_impls),*
194 };
195 Ok(instance)
196 }
197
198 }
199
200 };
201
202 generated_impl.into()
203}