tier 0.1.17

Rust configuration library for layered TOML, env, and CLI settings
Documentation
use std::collections::BTreeMap;
use std::sync::Arc;

use crate::error::ConfigError;
use crate::path::canonicalize_path_with_aliases;
use crate::{ConfigMetadata, EnvDecoder};

use super::canonical::canonicalize_runtime_path_across_layers;
use super::path::try_normalize_external_path;
use super::{CustomEnvDecoder, Layer};

pub(super) fn canonicalize_custom_env_decoders(
    decoders: &BTreeMap<String, CustomEnvDecoder>,
    metadata: &ConfigMetadata,
    layers: &[Layer],
) -> Result<BTreeMap<String, CustomEnvDecoder>, ConfigError> {
    let aliases = metadata.alias_overrides()?;
    let mut canonicalized = BTreeMap::new();
    let mut origins = BTreeMap::<String, String>::new();

    for (path, decoder) in decoders {
        let normalized = canonicalize_runtime_path_across_layers(
            &normalize_decoder_registration_path(path)?,
            layers,
        );
        let canonical = canonicalize_path_with_aliases(&normalized, &aliases);
        if let Some(first_path) = origins.get(&canonical)
            && first_path != &normalized
        {
            return Err(ConfigError::MetadataConflict {
                kind: "environment decoder",
                name: canonical,
                first_path: first_path.clone(),
                second_path: normalized,
            });
        }

        origins.insert(canonical.clone(), normalized);
        canonicalized.insert(canonical, Arc::clone(decoder));
    }

    Ok(canonicalized)
}

pub(super) fn canonicalize_env_decoders(
    decoders: &BTreeMap<String, EnvDecoder>,
    metadata: &ConfigMetadata,
    layers: &[Layer],
) -> Result<BTreeMap<String, EnvDecoder>, ConfigError> {
    let aliases = metadata.alias_overrides()?;
    let mut canonicalized = BTreeMap::new();
    let mut origins = BTreeMap::<String, (String, EnvDecoder)>::new();

    for (path, decoder) in decoders {
        let normalized = canonicalize_runtime_path_across_layers(
            &normalize_decoder_registration_path(path)?,
            layers,
        );
        let canonical = canonicalize_path_with_aliases(&normalized, &aliases);
        if let Some((first_path, first_decoder)) = origins.get(&canonical)
            && (first_path != &normalized || *first_decoder != *decoder)
        {
            return Err(ConfigError::MetadataConflict {
                kind: "environment decoder",
                name: canonical,
                first_path: first_path.clone(),
                second_path: normalized,
            });
        }

        origins.insert(canonical.clone(), (normalized, *decoder));
        canonicalized.insert(canonical, *decoder);
    }

    Ok(canonicalized)
}

fn normalize_decoder_registration_path(path: &str) -> Result<String, ConfigError> {
    let normalized =
        try_normalize_external_path(path).map_err(|message| ConfigError::MetadataInvalid {
            path: path.to_owned(),
            message: format!("invalid environment decoder path: {message}"),
        })?;
    if normalized.is_empty() {
        return Err(ConfigError::MetadataInvalid {
            path: path.to_owned(),
            message: "invalid environment decoder path: configuration path cannot be empty"
                .to_owned(),
        });
    }
    Ok(normalized)
}