use std::collections::BTreeMap;
use serde_json::Value;
use crate::{ConfigError, ConfigMetadata, FieldMetadata};
use super::super::Layer;
use super::checks::{
canonicalize_check_against_layers, canonicalize_check_against_value,
canonicalize_check_with_aliases,
};
use super::runtime::{
canonicalize_runtime_path, canonicalize_runtime_path_across_layers,
canonicalize_runtime_paths_across_layers, canonicalize_runtime_paths_against_value,
};
pub(in crate::loader) fn canonicalize_alias_overrides_against_value(
metadata: &ConfigMetadata,
value: &Value,
) -> Result<BTreeMap<String, String>, ConfigError> {
let aliases = metadata.alias_overrides()?;
let mut canonicalized = BTreeMap::new();
for (alias, canonical) in aliases {
let alias = canonicalize_runtime_path(value, &alias);
let canonical = canonicalize_runtime_path(value, &canonical);
if alias == canonical {
continue;
}
if let Some(first_path) = canonicalized.insert(alias.clone(), canonical.clone())
&& first_path != canonical
{
return Err(ConfigError::MetadataConflict {
kind: "alias",
name: alias,
first_path,
second_path: canonical,
});
}
}
Ok(canonicalized)
}
pub(in crate::loader) fn canonicalize_metadata_against_layers(
metadata: &ConfigMetadata,
layers: &[Layer],
) -> Result<ConfigMetadata, ConfigError> {
let fields = metadata
.fields()
.iter()
.cloned()
.map(|mut field| {
field.path = canonicalize_runtime_path_across_layers(&field.path, layers);
field.aliases = canonicalize_runtime_paths_across_layers(field.aliases, layers);
field
})
.collect::<Vec<_>>();
validate_canonical_explicit_env_targets(&fields)?;
let mut resolved = ConfigMetadata::new();
resolved.extend_fields(fields);
let aliases = resolved.alias_overrides()?;
let checks = metadata.checks().iter().cloned().map(|check| {
canonicalize_check_with_aliases(canonicalize_check_against_layers(check, layers), &aliases)
});
resolved.extend_checks(checks);
Ok(resolved)
}
pub(in crate::loader) fn canonicalize_metadata_against_value(
metadata: &ConfigMetadata,
value: &Value,
) -> Result<ConfigMetadata, ConfigError> {
let fields = metadata
.fields()
.iter()
.cloned()
.map(|mut field| {
field.path = canonicalize_runtime_path(value, &field.path);
field.aliases = canonicalize_runtime_paths_against_value(field.aliases, value);
field
})
.collect::<Vec<_>>();
validate_canonical_explicit_env_targets(&fields)?;
let mut resolved = ConfigMetadata::new();
resolved.extend_fields(fields);
let aliases = resolved.alias_overrides()?;
let checks = metadata.checks().iter().cloned().map(|check| {
canonicalize_check_with_aliases(canonicalize_check_against_value(check, value), &aliases)
});
resolved.extend_checks(checks);
Ok(resolved)
}
fn validate_canonical_explicit_env_targets(fields: &[FieldMetadata]) -> Result<(), ConfigError> {
let mut targets = BTreeMap::<String, String>::new();
for field in fields {
let Some(env) = &field.env else {
continue;
};
if let Some(first_env) = targets.insert(field.path.clone(), env.clone())
&& first_env != *env
{
return Err(ConfigError::MetadataConflict {
kind: "environment override target",
name: field.path.clone(),
first_path: first_env,
second_path: env.clone(),
});
}
}
Ok(())
}