use crate::error::Result;
use crate::value::ConfigValue;
use async_trait::async_trait;
use std::collections::HashMap;
pub mod env;
pub mod file;
pub mod remote;
pub use env::EnvSource;
pub use file::FileSource;
pub use remote::RemoteSource;
#[async_trait]
pub trait ConfigSource: Send + Sync {
async fn load(&self) -> Result<ConfigValue>;
fn name(&self) -> &str;
fn supports_watching(&self) -> bool {
false
}
async fn start_watching(&self) -> Result<tokio::sync::mpsc::Receiver<ConfigValue>> {
Err(crate::error::ConfigError::Other(
"Watching not supported by this source".to_string(),
))
}
}
pub struct CompositeSource {
sources: Vec<(Box<dyn ConfigSource>, u32)>, name: String,
}
impl CompositeSource {
pub fn new(name: String) -> Self {
Self {
sources: Vec::new(),
name,
}
}
pub fn add_source(mut self, source: Box<dyn ConfigSource>, priority: u32) -> Self {
self.sources.push((source, priority));
self.sources.sort_by(|a, b| a.1.cmp(&b.1)); self
}
}
#[async_trait]
impl ConfigSource for CompositeSource {
async fn load(&self) -> Result<ConfigValue> {
let mut merged_config = ConfigValue::Object(HashMap::new());
for (source, _priority) in &self.sources {
let config = source.load().await?;
merged_config.merge(config);
}
Ok(merged_config)
}
fn name(&self) -> &str {
&self.name
}
fn supports_watching(&self) -> bool {
self.sources
.iter()
.any(|(source, _)| source.supports_watching())
}
}
pub fn merge_config_values(mut base: ConfigValue, overlay: ConfigValue) -> ConfigValue {
base.merge(overlay);
base
}