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 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}