1mod config;
2
3use darling::ast::NestedMeta;
4use darling::{Error, FromMeta};
5use proc_macro::TokenStream;
6use quote::quote;
7use syn::ItemStruct;
8
9#[derive(thiserror::Error, Debug)]
10enum ConfineError {
11 #[error("Error parsing attribute arguments: {0}")]
12 ParseError(#[from] Error),
13
14 #[error("Error parsing macro input: {0}")]
15 ParseMacroInput(#[from] syn::Error),
16
17 #[error("The variable to configure the environment ('{0}') was not set.")]
18 EnvVarNotSet(String),
19
20 #[error("Unsupported type: {0}. Currently only supports String, i64, f64, and bool.")]
21 UnsupportedType(String),
22
23 #[error("Config file error: {0}")]
24 ConfigBuilderError(#[from] confine_builder::ConfineBuilderError),
25}
26
27impl From<ConfineError> for TokenStream {
28 fn from(value: ConfineError) -> Self {
29 let error = Error::custom(value);
30 TokenStream::from(error.write_errors())
31 }
32}
33
34#[derive(Debug, FromMeta)]
35struct ConfineArgs {
36 pub default_env: Option<String>,
37 env_var: Option<String>,
38 path: Option<String>,
39 prefix: Option<String>,
40}
41
42impl ConfineArgs {
43 fn env_var(&self) -> String {
44 self.env_var.clone().unwrap_or("CONFINE_ENV".to_string())
45 }
46
47 fn path(&self) -> String {
48 self.path.clone().unwrap_or("config".into())
49 }
50
51 fn prefix(&self) -> String {
52 self.prefix.clone().unwrap_or("application".to_string())
53 }
54}
55
56#[proc_macro_attribute]
57pub fn confine(args: TokenStream, input: TokenStream) -> TokenStream {
58 process_confine(args, input).unwrap_or_else(|e| e.into())
59}
60
61fn process_confine(args: TokenStream, input: TokenStream) -> Result<TokenStream, ConfineError> {
62 let attr_args = NestedMeta::parse_meta_list(args.into())?;
63 let args = ConfineArgs::from_list(&attr_args)?;
64 let input = syn::parse::<ItemStruct>(input)?;
65 let struct_name = &input.ident;
66
67 let path = args.path();
68 let env_var = args.env_var();
69 let prefix = args.prefix();
70
71 Ok(quote!(
72 #input
73
74 impl #struct_name {
75 pub fn try_load() -> Result<Self, confine::ConfineBuilderError> {
76 let config = confine::ConfineConfigBuilder::default()
77 .config_path(#path.into())
78 .env_var(#env_var.into())
79 .prefix(#prefix.into())
80 .try_load::<#struct_name>()?;
81 Ok(config)
82 }
83 }
84 )
85 .into())
86}