use figment::{
providers::{Env, Format, Json, Serialized, Toml, Yaml},
Figment,
};
use serde::{Deserialize, Serialize};
use std::{any::Any, fmt::Debug, marker::PhantomData, path::PathBuf, sync::Arc};
pub trait ConfigLoader: Any + Send + Sync {
fn load_env(&self, env: Env) -> Env;
}
pub trait Config:
Any + Clone + Debug + Default + Serialize + Send + Sync + for<'a> Deserialize<'a>
{
}
pub struct Loader<C: Config> {
loaders: Vec<Arc<dyn ConfigLoader>>,
_phantom: PhantomData<C>,
}
impl<C: Config> Loader<C> {
pub fn new(loaders: Vec<Arc<dyn ConfigLoader>>) -> Self {
Self {
loaders,
_phantom: Default::default(),
}
}
pub fn load(&self, custom_path: Option<PathBuf>) -> figment::error::Result<C> {
let mut config = Figment::new()
.merge(Serialized::defaults(C::default()))
.merge(Toml::file("config.toml"))
.merge(Yaml::file("config.yml"))
.merge(Yaml::file("config.yaml"))
.merge(Json::file("config.json"));
if let Some(path) = custom_path {
if let Some(path_str) = path.to_str() {
if path_str.ends_with(".toml") {
config = config.merge(Toml::file(path_str));
} else if path_str.ends_with(".yml") || path_str.ends_with(".yaml") {
config = config.merge(Yaml::file(path_str));
} else if path_str.ends_with(".json") {
config = config.merge(Json::file(path_str));
}
}
}
let mut env = Env::raw();
for loader in &self.loaders {
env = loader.load_env(env);
}
config = config.merge(env);
config.extract()
}
}