use crate::cfg::cfg_error::CfgError;
use crate::env::{APP_ENV, AppEnv, EnvError};
use config::builder::DefaultState;
use config::{Config, ConfigBuilder};
use notify::{RecommendedWatcher, RecursiveMode};
use notify_debouncer_mini::{DebounceEventResult, Debouncer, new_debouncer};
use std::path::Path;
use std::sync::{Arc, mpsc};
use std::time::Duration;
pub fn build_cfg<'a, T: serde::Deserialize<'a>>(
env_var_prefix: &str,
cfg_file_name_without_ext: Option<&str>,
cfg_file_path: Option<String>,
) -> Result<(T, Vec<String>), CfgError> {
let mut config = Config::builder();
let mut files = vec![];
config = if let Some(cfg_file_path) = cfg_file_path.clone() {
add_source(config, cfg_file_path.as_str(), None, &mut files)
} else {
let AppEnv {
app_dir,
app_file_name_without_ext,
..
} = APP_ENV.get().ok_or(EnvError::GetAppEnv())?;
let temp_path = app_dir
.join(
if let Some(cfg_file_name_without_ext) = cfg_file_name_without_ext {
cfg_file_name_without_ext
} else {
app_file_name_without_ext
},
)
.to_string_lossy()
.to_string();
config = add_source(config, temp_path.as_str(), Some("toml"), &mut files);
config = add_source(config, temp_path.as_str(), Some("yml"), &mut files);
config = add_source(config, temp_path.as_str(), Some("json"), &mut files);
config = add_source(config, temp_path.as_str(), Some("ini"), &mut files);
config = add_source(config, temp_path.as_str(), Some("ron"), &mut files);
config
};
let config = config
.add_source(config::Environment::with_prefix(env_var_prefix))
.build()
.map_err(CfgError::Build)?;
Ok((
config.try_deserialize().map_err(CfgError::Deserialize)?,
files,
))
}
fn add_source(
config: ConfigBuilder<DefaultState>,
file_path_without_ext: &str,
ext: Option<&str>,
files: &mut Vec<String>,
) -> ConfigBuilder<DefaultState> {
let file_path_string = if let Some(ext) = ext {
format!("{file_path_without_ext}.{ext}")
} else {
file_path_without_ext.to_string()
};
let file_path = Path::new(file_path_string.as_str());
if !file_path.exists() {
return config;
}
files.push(file_path_string.clone());
let file = config::File::with_name(file_path_string.as_str());
config.add_source(file)
}
pub fn watch_cfg_file(
files: Arc<Vec<String>>,
) -> Result<
(
Debouncer<RecommendedWatcher>,
mpsc::Receiver<DebounceEventResult>,
),
notify::Error,
> {
let (sender, receiver) = mpsc::channel();
let mut debouncer = new_debouncer(
Duration::from_millis(500), sender,
)?;
let watcher = debouncer.watcher();
for file in &*files {
watcher.watch(Path::new(&file), RecursiveMode::NonRecursive)?;
}
Ok((debouncer, receiver))
}