use std::path::PathBuf;
use crate::config::{ErrorMode, LoadOptions, MergeStrategy};
use crate::domain::{Format, Result, SourceError};
use super::{CascadeLayer, CascadeLoader, CascadeScope};
#[derive(Debug, Clone, Default)]
pub struct CascadeLayerBuilder {
scope: Option<CascadeScope>,
path: Option<PathBuf>,
optional: bool,
priority: i32,
format: Option<Format>,
}
impl CascadeLayerBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn scope(mut self, scope: CascadeScope) -> Self {
self.scope = Some(scope);
self
}
#[must_use]
pub fn path(mut self, path: impl Into<PathBuf>) -> Self {
self.path = Some(path.into());
self
}
#[must_use]
pub const fn optional(mut self, optional: bool) -> Self {
self.optional = optional;
self
}
#[must_use]
pub const fn priority(mut self, priority: i32) -> Self {
self.priority = priority;
self
}
#[must_use]
pub const fn format(mut self, format: Format) -> Self {
self.format = Some(format);
self
}
pub fn build(self) -> Result<CascadeLayer> {
let scope = self
.scope
.ok_or_else(|| SourceError::validation("Cascade layer scope is required"))?;
let path = self
.path
.ok_or_else(|| SourceError::validation("Cascade layer path is required"))?;
Ok(CascadeLayer {
scope,
path,
optional: self.optional,
priority: self.priority,
format: self.format,
})
}
}
#[derive(Debug, Clone, Default)]
pub struct CascadeLoaderBuilder {
layers: Vec<CascadeLayer>,
options: Option<LoadOptions>,
default_format: Option<Format>,
}
impl CascadeLoaderBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn add_layer(mut self, layer: CascadeLayer) -> Self {
self.layers.push(layer);
self
}
#[must_use]
pub fn add_file(mut self, scope: CascadeScope, path: impl Into<PathBuf>) -> Self {
self.layers.push(CascadeLayer::new(scope, path));
self
}
#[must_use]
pub fn add_optional_file(mut self, scope: CascadeScope, path: impl Into<PathBuf>) -> Self {
self.layers
.push(CascadeLayer::new(scope, path).optional(true));
self
}
#[cfg(feature = "file")]
#[must_use]
pub fn discover_app(mut self, app_name: impl AsRef<str>) -> Self {
let options = self.options.clone().unwrap_or_default();
let extensions: Vec<&str> = options.extensions.iter().map(String::as_str).collect();
let pattern = cfgmatic_paths::FilePattern::extensions(options.base_name, &extensions);
let finder = cfgmatic_paths::PathsBuilder::new(app_name.as_ref()).build();
for candidate in finder
.find_config_files(&pattern)
.into_iter()
.filter(cfgmatic_paths::ConfigCandidate::is_file)
{
let scope = match candidate.tier {
cfgmatic_paths::ConfigTier::System => CascadeScope::System,
cfgmatic_paths::ConfigTier::Local => CascadeScope::Local,
cfgmatic_paths::ConfigTier::User => CascadeScope::User,
};
self.layers.push(
CascadeLayer::new(scope, candidate.path)
.priority(i32::from(u8::from(candidate.tier))),
);
}
self
}
#[must_use]
pub fn options(mut self, options: LoadOptions) -> Self {
self.options = Some(options);
self
}
#[must_use]
pub fn merge_strategy(mut self, merge_strategy: MergeStrategy) -> Self {
let mut options = self.options.unwrap_or_default();
options.merge_strategy = merge_strategy;
self.options = Some(options);
self
}
#[must_use]
pub fn fail_fast(mut self, fail_fast: bool) -> Self {
let mut options = self.options.unwrap_or_default();
options.error_mode = ErrorMode::from_fail_fast(fail_fast);
self.options = Some(options);
self
}
#[must_use]
pub const fn default_format(mut self, default_format: Format) -> Self {
self.default_format = Some(default_format);
self
}
#[must_use]
pub fn build(self) -> CascadeLoader {
CascadeLoader {
layers: self.layers,
options: self.options.unwrap_or_default(),
default_format: self.default_format,
}
}
}