use std::fmt::Debug; use std::str::FromStr; #[cfg(feature = "async")] use async_trait::async_trait; use crate::error::Result; use crate::map::Map; use crate::path; use crate::value::{Value, ValueKind}; /// Describes a generic _source_ of configuration properties. pub trait Source: Debug { fn clone_into_box(&self) -> Box; /// Collect all configuration properties available from this source and return /// a Map. fn collect(&self) -> Result>; /// Collects all configuration properties to a provided cache. fn collect_to(&self, cache: &mut Value) -> Result<()> { self.collect()? .iter() .for_each(|(key, val)| set_value(cache, key, val)); Ok(()) } } fn set_value(cache: &mut Value, key: &str, value: &Value) { match path::Expression::from_str(key) { // Set using the path Ok(expr) => expr.set(cache, value.clone()), // Set diretly anyway _ => path::Expression::Identifier(key.to_string()).set(cache, value.clone()), } } /// Describes a generic _source_ of configuration properties capable of using an async runtime. /// /// At the moment this library does not implement it, although it allows using its implementations /// within builders. Due to the scattered landscape of asynchronous runtimes, it is impossible to /// cater to all needs with one implementation. Also, this trait might be most useful with remote /// configuration sources, reachable via the network, probably using HTTP protocol. Numerous HTTP /// libraries exist, making it even harder to find one implementation that rules them all. /// /// For those reasons, it is left to other crates to implement runtime-specific or proprietary /// details. /// /// It is advised to use `async_trait` crate while implementing this trait. /// /// See examples for sample implementation. #[cfg(feature = "async")] #[async_trait] pub trait AsyncSource: Debug + Sync { // Sync is supertrait due to https://docs.rs/async-trait/0.1.50/async_trait/index.html#dyn-traits /// Collects all configuration properties available from this source and return /// a Map as an async operations. async fn collect(&self) -> Result>; /// Collects all configuration properties to a provided cache. async fn collect_to(&self, cache: &mut Value) -> Result<()> { self.collect() .await? .iter() .for_each(|(key, val)| set_value(cache, key, val)); Ok(()) } } #[cfg(feature = "async")] impl Clone for Box { fn clone(&self) -> Self { self.to_owned() } } impl Clone for Box { fn clone(&self) -> Self { self.clone_into_box() } } impl Source for Vec> { fn clone_into_box(&self) -> Box { Box::new((*self).clone()) } fn collect(&self) -> Result> { let mut cache: Value = Map::::new().into(); for source in self { source.collect_to(&mut cache)?; } if let ValueKind::Table(table) = cache.kind { Ok(table) } else { unreachable!(); } } } impl Source for [Box] { fn clone_into_box(&self) -> Box { Box::new(self.to_owned()) } fn collect(&self) -> Result> { let mut cache: Value = Map::::new().into(); for source in self { source.collect_to(&mut cache)?; } if let ValueKind::Table(table) = cache.kind { Ok(table) } else { unreachable!(); } } } impl Source for Vec where T: Source + Sync + Send + Clone + 'static, { fn clone_into_box(&self) -> Box { Box::new((*self).clone()) } fn collect(&self) -> Result> { let mut cache: Value = Map::::new().into(); for source in self { source.collect_to(&mut cache)?; } if let ValueKind::Table(table) = cache.kind { Ok(table) } else { unreachable!(); } } }