mod aliases_pass;
mod environment;
mod origin;
mod recover;
mod sections;
use figment::value::Dict;
use figment::Figment;
use std::path::Path;
use serde::de::DeserializeOwned;
use crate::error::{Error, Origin};
use crate::layer::{DEFAULTS_NAME, FLAGS_NAME, OVERRIDES_NAME};
use crate::snapshot::Snapshot;
use crate::source::LoadSpec;
use aliases_pass::apply_aliases;
use environment::{environment, merge_env_files};
use origin::convert;
pub(crate) use recover::recover;
pub(crate) const CACHED_NAME: &str = "the last configuration that worked";
const REMOTE_PREFIX: &str = "the remote store ";
pub(crate) fn load<T: DeserializeOwned>(spec: &LoadSpec<'_>) -> Result<T, Error> {
apply_aliases(build(spec)?, spec)
.select(spec.key)
.extract()
.map_err(convert)
}
pub(crate) fn snapshot(spec: &LoadSpec<'_>) -> Result<Snapshot, Error> {
resolved(spec).map(|(snapshot, _figment)| snapshot)
}
pub(crate) fn resolved(spec: &LoadSpec<'_>) -> Result<(Snapshot, Figment), Error> {
let figment = apply_aliases(build(spec)?, spec).select(spec.key);
let snapshot = figment
.extract::<Dict>()
.map(Snapshot::new)
.map_err(convert)?;
Ok((snapshot, figment))
}
pub(crate) fn origin_in(figment: &Figment, path: &str) -> Origin {
figment
.find_metadata(path)
.map_or(Origin::Unknown, origin::origin_of)
}
pub(crate) fn source_of(spec: &LoadSpec<'_>, path: &str) -> Result<Option<Origin>, Error> {
let figment = apply_aliases(build(spec)?, spec).select(spec.key);
Ok(figment.find_metadata(path).map(origin::origin_of))
}
pub(crate) fn is_set(spec: &LoadSpec<'_>, path: &str) -> Result<bool, Error> {
Ok(apply_aliases(build(spec)?, spec)
.select(spec.key)
.contains(path))
}
fn build(spec: &LoadSpec<'_>) -> Result<Figment, Error> {
let mut figment = Figment::new();
if let Some(defaults) = spec.defaults {
figment = figment.merge(defaults.provider(spec.key, DEFAULTS_NAME));
}
let profile = sections::validated_profile(spec)?;
if let Some(search) = &spec.search {
for (path, format) in search.resolve() {
figment = sections::merge_file(figment, &path, format)?;
figment = sections::merge_profile_variant(figment, &path, format, profile.as_deref())?;
}
}
for source in spec.sources {
figment = sections::merge(figment, source)?;
if let (Some(path), Some(format)) = (source.path(), source.format()) {
figment = sections::merge_profile_variant(
figment,
Path::new(path),
format,
profile.as_deref(),
)?;
}
}
if let Some(remote) = spec.remote {
if let Some(document) = remote.document() {
let name = format!(
"{REMOTE_PREFIX}{}",
remote.describe().unwrap_or_else(|| "(unnamed)".to_owned())
);
figment =
sections::merge_named_text(figment, &document.text, document.format, &name, None)?;
}
}
figment = merge_env_files(figment, spec)?;
if let Some(prefix) = spec.full_env_prefix() {
figment = figment.merge(environment(
&prefix,
spec.key,
spec.nest,
spec.allow_empty_env,
));
}
if let Some(bindings) = spec.env_bindings {
for binding in bindings.providers(spec.key, spec.allow_empty_env) {
figment = figment.merge(binding);
}
}
if let Some(flags) = spec.flags {
figment = figment.merge(flags.provider(spec.key, FLAGS_NAME));
}
if let Some(overrides) = spec.overrides {
figment = figment.merge(overrides.provider(spec.key, OVERRIDES_NAME));
}
Ok(figment)
}