distributed_config/sources/
mod.rs1use crate::error::Result;
4use crate::value::ConfigValue;
5use async_trait::async_trait;
6use std::collections::HashMap;
7
8pub mod env;
9pub mod file;
10pub mod remote;
11
12pub use env::EnvSource;
13pub use file::FileSource;
14pub use remote::RemoteSource;
15
16#[async_trait]
18pub trait ConfigSource: Send + Sync {
19 async fn load(&self) -> Result<ConfigValue>;
21
22 fn name(&self) -> &str;
24
25 fn supports_watching(&self) -> bool {
27 false
28 }
29
30 async fn start_watching(&self) -> Result<tokio::sync::mpsc::Receiver<ConfigValue>> {
32 Err(crate::error::ConfigError::Other(
33 "Watching not supported by this source".to_string(),
34 ))
35 }
36}
37
38pub struct CompositeSource {
40 sources: Vec<(Box<dyn ConfigSource>, u32)>, name: String,
42}
43
44impl CompositeSource {
45 pub fn new(name: String) -> Self {
47 Self {
48 sources: Vec::new(),
49 name,
50 }
51 }
52
53 pub fn add_source(mut self, source: Box<dyn ConfigSource>, priority: u32) -> Self {
55 self.sources.push((source, priority));
56 self.sources.sort_by(|a, b| a.1.cmp(&b.1)); self
58 }
59}
60
61#[async_trait]
62impl ConfigSource for CompositeSource {
63 async fn load(&self) -> Result<ConfigValue> {
64 let mut merged_config = ConfigValue::Object(HashMap::new());
65
66 for (source, _priority) in &self.sources {
68 let config = source.load().await?;
69 merged_config.merge(config);
70 }
71
72 Ok(merged_config)
73 }
74
75 fn name(&self) -> &str {
76 &self.name
77 }
78
79 fn supports_watching(&self) -> bool {
80 self.sources
81 .iter()
82 .any(|(source, _)| source.supports_watching())
83 }
84}
85
86pub fn merge_config_values(mut base: ConfigValue, overlay: ConfigValue) -> ConfigValue {
88 base.merge(overlay);
89 base
90}