use std::collections::BTreeMap;
use super::super::{
ConfigMetadata, EffectiveSourcePolicy, EffectiveValidation, FieldMetadata, MetadataMatchScore,
};
use crate::metadata::paths::{metadata_match_score, try_normalize_metadata_path};
impl ConfigMetadata {
#[must_use]
pub fn fields(&self) -> &[FieldMetadata] {
&self.fields
}
#[must_use]
pub fn checks(&self) -> &[super::super::ValidationCheck] {
&self.checks
}
#[must_use]
pub fn field(&self, path: &str) -> Option<&FieldMetadata> {
let normalized = try_normalize_metadata_path(path).ok()?;
let mut best = None::<(MetadataMatchScore, &FieldMetadata)>;
for field in &self.fields {
for candidate in
std::iter::once(field.path.as_str()).chain(field.aliases.iter().map(String::as_str))
{
let Some(score) = metadata_match_score(&normalized, candidate) else {
continue;
};
match &mut best {
Some((best_score, best_field)) if score > *best_score => {
*best_score = score;
*best_field = field;
}
None => best = Some((score, field)),
_ => {}
}
}
}
best.map(|(_, field)| field)
}
pub(crate) fn matching_fields_for_path(&self, path: &str) -> Vec<&FieldMetadata> {
let normalized = match try_normalize_metadata_path(path) {
Ok(normalized) => normalized,
Err(_) => return Vec::new(),
};
let mut matches = self
.fields
.iter()
.filter_map(|field| {
let best = std::iter::once(field.path.as_str())
.chain(field.aliases.iter().map(String::as_str))
.filter_map(|candidate| metadata_match_score(&normalized, candidate))
.max();
best.map(|score| (score, field))
})
.collect::<Vec<_>>();
matches.sort_by(|left, right| {
left.0
.cmp(&right.0)
.then_with(|| left.1.path.cmp(&right.1.path))
});
matches.into_iter().map(|(_, field)| field).collect()
}
pub(crate) fn effective_source_policy_for(&self, path: &str) -> Option<EffectiveSourcePolicy> {
let mut policy = EffectiveSourcePolicy::default();
let mut has_policy = false;
for field in self.matching_fields_for_path(path) {
if field.allowed_sources.is_some() || field.denied_sources.is_some() {
has_policy = true;
policy.apply_field(field);
}
}
has_policy.then_some(policy)
}
pub(crate) fn effective_validations_for(&self, path: &str) -> Vec<EffectiveValidation> {
let Some(field) = self.effective_field_for(path) else {
return Vec::new();
};
field
.validations
.iter()
.cloned()
.map(|rule| EffectiveValidation {
field: field.clone(),
rule,
})
.collect()
}
pub(crate) fn effective_field_for(&self, path: &str) -> Option<FieldMetadata> {
let mut matches = self.matching_fields_for_path(path).into_iter();
let mut effective = matches.next()?.clone();
for field in matches {
effective.merge_from(field.clone());
}
Some(effective)
}
#[must_use]
pub fn fields_by_path(&self) -> BTreeMap<String, FieldMetadata> {
self.fields
.iter()
.cloned()
.map(|field| (field.path.clone(), field))
.collect()
}
#[must_use]
pub fn secret_paths(&self) -> Vec<String> {
self.fields
.iter()
.filter(|field| field.secret)
.map(|field| field.path.clone())
.collect()
}
}