use std::path::PathBuf; use crate::app::AppConfig; use crate::error::{ConfigError, ConfigResult}; #[derive(Debug, Clone)] pub struct ConfigLoader { config_file: Option, local_config_file: Option, env_prefix: String, use_env: bool, use_dotenv: bool, validate: bool, } impl Default for ConfigLoader { fn default() -> Self { Self::new() } } impl ConfigLoader { pub fn new() -> Self { Self { config_file: Some(PathBuf::from("config/default.yaml")), local_config_file: Some(PathBuf::from("config/local.yaml")), env_prefix: "APP".to_string(), use_env: true, use_dotenv: true, validate: true, } } pub fn config_file(mut self, path: impl Into) -> Self { self.config_file = Some(path.into()); self } pub fn local_config_file(mut self, path: impl Into) -> Self { self.local_config_file = Some(path.into()); self } pub fn no_config_file(mut self) -> Self { self.config_file = None; self } pub fn no_local_config_file(mut self) -> Self { self.local_config_file = None; self } pub fn env_prefix(mut self, prefix: impl Into) -> Self { self.env_prefix = prefix.into(); self } pub fn no_env(mut self) -> Self { self.use_env = false; self } pub fn no_dotenv(mut self) -> Self { self.use_dotenv = false; self } pub fn validate(mut self, on: bool) -> Self { self.validate = on; self } pub fn load(self) -> ConfigResult { if self.use_dotenv { let _ = dotenvy::dotenv(); } let mut builder = config_rs::Config::builder(); if let Some(path) = self.config_file { builder = builder .add_source(config_rs::File::with_name(&path.to_string_lossy()).required(false)); } if let Some(path) = self.local_config_file { builder = builder .add_source(config_rs::File::with_name(&path.to_string_lossy()).required(false)); } if self.use_env { builder = builder.add_source( config_rs::Environment::with_prefix(&self.env_prefix) .prefix_separator("_") .separator("__") .try_parsing(true), ); } let conf = builder.build()?; let app: AppConfig = conf.try_deserialize()?; if self.validate { app.validate().map_err(ConfigError::Validation)?; } Ok(app) } } pub fn load() -> ConfigResult { ConfigLoader::new().load() }