mod file;
mod env;
mod proxy;
pub mod error;
#[cfg(test)]
mod tests;
pub use error::ConfigError;
pub use file::FileConfigProvider;
pub use env::EnvConfigProvider;
use std::fmt::Debug;
use std::sync::Arc;
use serde::de::DeserializeOwned;
use serde_json::Value;
pub trait ConfigProvider: Debug + Send + Sync {
fn has(&self, key: &str) -> bool;
fn provider_name(&self) -> &str;
fn get_raw(&self, key: &str) -> Result<Option<Value>, ConfigError>;
}
pub trait ConfigProviderExt: ConfigProvider {
fn get<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, ConfigError> {
match self.get_raw(key)? {
Some(value) => {
serde_json::from_value(value)
.map(Some)
.map_err(|e| ConfigError::ParseError(format!("failed to deserialize '{}': {}", key, e)))
},
None => Ok(None),
}
}
}
impl<T: ConfigProvider> ConfigProviderExt for T {}
#[derive(Debug, Default)]
pub struct ConfigBuilder {
providers: Vec<Arc<dyn ConfigProvider>>,
}
impl ConfigBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn with_provider<P: ConfigProvider + 'static>(mut self, provider: P) -> Self {
self.providers.push(Arc::new(provider));
self
}
pub fn build(self) -> Config {
Config {
providers: self.providers,
}
}
}
#[derive(Debug, Clone)]
pub struct Config {
providers: Vec<Arc<dyn ConfigProvider>>,
}
impl Config {
pub fn builder() -> ConfigBuilder {
ConfigBuilder::new()
}
fn get_raw(&self, key: &str) -> Result<Option<Value>, ConfigError> {
for provider in self.providers.iter().rev() {
if provider.has(key) {
return provider.get_raw(key);
}
}
Ok(None)
}
pub fn get<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, ConfigError> {
match self.get_raw(key)? {
Some(value) => {
serde_json::from_value(value)
.map(Some)
.map_err(|e| ConfigError::ParseError(format!("failed to deserialize '{}': {}", key, e)))
},
None => Ok(None),
}
}
pub fn get_or_default<T: DeserializeOwned>(&self, key: &str, default: T) -> Result<T, ConfigError> {
match self.get(key)? {
Some(value) => Ok(value),
None => Ok(default),
}
}
pub fn default_file(file_path: &str) -> Result<Self, ConfigError> {
let provider = FileConfigProvider::new(file_path)?;
Ok(Self::builder().with_provider(provider).build())
}
}