use std::str::FromStr;
use crate::config::Config;
use crate::errors::Result;
use crate::map::Map;
use crate::path::Expression;
#[cfg(feature = "async")]
use crate::source::AsyncSource;
use crate::source::Source;
use crate::value::Value;
use crate::{ConfigError, File};
#[derive(Debug, Clone, Default)]
#[must_use]
pub struct ConfigBuilder<St: BuilderState> {
defaults: Map<Expression, Value>,
overrides: Map<Expression, Value>,
state: St,
}
pub trait BuilderState {}
#[derive(Debug, Default, Clone)]
pub struct DefaultState {
sources: Vec<Box<dyn Source + Send + Sync>>,
}
#[deprecated = "AsyncConfigBuilder is useless. Use ConfigBuilder<AsyncState>"]
#[doc(hidden)]
#[derive(Debug, Clone, Default)]
pub struct AsyncConfigBuilder {}
#[derive(Debug, Default, Clone)]
pub struct AsyncState {
sources: Vec<SourceType>,
}
#[derive(Debug, Clone)]
enum SourceType {
Sync(Box<dyn Source + Send + Sync>),
#[cfg(feature = "async")]
Async(Box<dyn AsyncSource + Send + Sync>),
}
impl BuilderState for DefaultState {}
impl BuilderState for AsyncState {}
impl<St: BuilderState> ConfigBuilder<St> {
pub fn set_default<S, T>(mut self, key: S, value: T) -> Result<Self>
where
S: AsRef<str>,
T: Into<Value>,
{
self.defaults.insert(Expression::from_str(key.as_ref())?, value.into());
Ok(self)
}
pub fn set_override<S, T>(mut self, key: S, value: T) -> Result<Self>
where
S: AsRef<str>,
T: Into<Value>,
{
self.overrides.insert(Expression::from_str(key.as_ref())?, value.into());
Ok(self)
}
pub fn set_override_option<S, T>(mut self, key: S, value: Option<T>) -> Result<Self>
where
S: AsRef<str>,
T: Into<Value>,
{
if let Some(value) = value {
self.overrides.insert(Expression::from_str(key.as_ref())?, value.into());
}
Ok(self)
}
}
impl ConfigBuilder<DefaultState> {
pub fn add_source<T>(mut self, source: T) -> Self
where
T: Source + Send + Sync + 'static,
{
self.state.sources.push(Box::new(source));
self
}
#[cfg(feature = "async")]
pub fn add_async_source<T>(self, source: T) -> ConfigBuilder<AsyncState>
where
T: AsyncSource + Send + Sync + 'static,
{
let async_state = ConfigBuilder {
state: AsyncState { sources: self.state.sources.into_iter().map(SourceType::Sync).collect() },
defaults: self.defaults,
overrides: self.overrides,
};
async_state.add_async_source(source)
}
pub fn build(self) -> Result<Config> {
Self::build_internal(self.defaults, self.overrides, &self.state.sources)
}
pub fn build_cloned(&self) -> Result<Config> {
Self::build_internal(self.defaults.clone(), self.overrides.clone(), &self.state.sources)
}
fn build_internal(
defaults: Map<Expression, Value>,
overrides: Map<Expression, Value>,
sources: &[Box<dyn Source + Send + Sync>],
) -> Result<Config> {
let mut cache: Value = Map::<String, Value>::new().into();
for (key, val) in defaults {
key.set(&mut cache, val);
}
sources.collect_to(&mut cache)?;
for (key, val) in overrides {
key.set(&mut cache, val);
}
Ok(Config::new(cache))
}
}
impl ConfigBuilder<AsyncState> {
pub fn add_source<T>(mut self, source: T) -> Self
where
T: Source + Send + Sync + 'static,
{
self.state.sources.push(SourceType::Sync(Box::new(source)));
self
}
#[cfg(feature = "async")]
pub fn add_async_source<T>(mut self, source: T) -> Self
where
T: AsyncSource + Send + Sync + 'static,
{
self.state.sources.push(SourceType::Async(Box::new(source)));
self
}
pub async fn build(self) -> Result<Config> {
let mut config =
Self::build_internal(self.defaults, self.overrides, &self.state.sources).await?;
if let Ok(extends) = config.get_string("extends") {
let parent_config =
Config::builder().add_source(File::with_name(&extends).required(true)).build();
match parent_config {
Ok(parent_config) => {
let result = config.merge(parent_config);
if let Err(e) = result {
return Err(ConfigError::FileParse { uri: Some(extends).into(), cause: Box::new(e) });
}
return Ok(config);
}
Err(e) => {
return Err(ConfigError::FileParse { uri: Some(extends).into(), cause: Box::new(e) });
}
}
}
if let Ok(extends) = config.get_array("extends") {
for item in extends.iter() {
if let Ok(extend_path) = item.clone().into_string() {
let parent_config = Config::builder()
.add_source(File::with_name(extend_path.as_str()).required(true))
.build();
if let Err(e) = parent_config {
return Err(ConfigError::FileParse {
uri: Some(extend_path).into(),
cause: Box::new(e),
});
}
config = config.with_merged(parent_config.unwrap())?;
} else {
return Err(ConfigError::InvalidExtendFormat);
}
}
}
Ok(config)
}
pub async fn build_cloned(&self) -> Result<Config> {
Self::build_internal(self.defaults.clone(), self.overrides.clone(), &self.state.sources).await
}
async fn build_internal(
defaults: Map<Expression, Value>,
overrides: Map<Expression, Value>,
sources: &[SourceType],
) -> Result<Config> {
let mut cache: Value = Map::<String, Value>::new().into();
for (key, val) in defaults {
key.set(&mut cache, val);
}
for source in sources.iter() {
match source {
SourceType::Sync(source) => source.collect_to(&mut cache)?,
#[cfg(feature = "async")]
SourceType::Async(source) => source.collect_to(&mut cache).await?,
}
}
for (key, val) in overrides {
key.set(&mut cache, val);
}
Ok(Config::new(cache))
}
}