net_config/
lib.rs

1use proc_macro::TokenStream;
2
3use quote::quote;
4use syn::{DeriveInput, parse_macro_input};
5
6#[proc_macro_derive(NetConfig)]
7pub fn derive(input: TokenStream) -> TokenStream {
8    let input = parse_macro_input!(input);
9    let DeriveInput { ident, .. } = input;
10
11    // Create a new ident for the new struct name.
12    // let new_ident = syn::Ident::new("MyNewStruct", ident.span());
13
14    let output = quote! {
15        pub use net_file;
16
17        const CONFIG_DIR: &str = ".config";
18        const PKG_NAME: &str = std::env!("CARGO_PKG_NAME");
19
20        impl #ident {
21            pub fn new(path: &str) -> NetConfigBuilder {
22                NetConfigBuilder::default().with_config_dir(path.to_string())
23            }
24
25            pub fn builder() -> NetConfigBuilder {
26                NetConfigBuilder::default()
27            }
28        }
29
30        impl std::fmt::Display for #ident {
31            fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
32                write!(f, "{}", to_string(self).unwrap())
33            }
34        }
35
36        #[derive(std::fmt::Debug)]
37        pub struct NetConfigBuilder {
38            config_path: std::path::PathBuf,
39        }
40
41        #[cfg(debug_assertions)]
42        impl Default for NetConfigBuilder {
43            fn default() -> Self {
44                NetConfigBuilder { config_path: std::path::PathBuf::new().join(PKG_NAME).join(CONFIG_DIR) }
45            }
46        }
47
48        #[cfg(not(debug_assertions))]
49        impl Default for NetConfigBuilder {
50            fn default() -> Self {
51                if env::var("NET_CONFIG_DIR").is_ok() {
52                    return NetConfigBuilder { config_path: std::path::PathBuf::from(&env::var("NET_CONFIG_DIR").unwrap()) };
53                }
54
55                let base_dir = Self::get_base_dir().unwrap();
56                NetConfigBuilder { config_path: std::path::PathBuf::from(base_dir.home_dir()).join(CONFIG_DIR).join(PKG_NAME) }
57            }
58        }
59
60        impl NetConfigBuilder {
61            pub(crate) fn with_config_dir(mut self, config_path: String) -> Self {
62                self.config_path = std::path::PathBuf::from(config_path);
63                self
64            }
65
66            #[cfg(not(debug_assertions))]
67            fn get_base_dir() -> Option<directories::BaseDirs> {
68                directories::BaseDirs::new()
69            }
70        }
71
72        impl<'de> NetConfigBuilder {
73            pub fn build(&self) -> Result<#ident, config::ConfigError> {
74                log::debug!("{:?}", self);
75
76                let config_files = net_file::file::files::Files::find_files(&self.config_path, "toml");
77                log::debug!("found config files {:?}", config_files);
78
79                match Self::create_config(config_files) {
80                    Ok(config) => { config.try_deserialize::<'de, #ident>() }
81                    Err(e) => { Err(e) }
82                }
83            }
84
85            fn create_config(config_files: Vec<std::path::PathBuf>) -> Result<config::Config, config::ConfigError> {
86                use std::ops::Deref;
87
88                let mut builder = config::Config::builder();
89
90                for i in 0..config_files.len() {
91                    let path_buf = config_files.get(i).unwrap().deref();
92                    builder = builder.add_source(config::File::from(path_buf));
93                }
94
95                builder = builder.add_source(config::Environment::with_prefix("net"));
96                builder = builder.add_source(config::Environment::with_prefix("net").separator("DOT"));
97                let config = builder.build();
98                log::info!("{:?}", config);
99
100                config
101
102            }
103        }
104    };
105
106    output.into()
107}