use super::alias::{registry_for, resolve_alias, undefined_alias};
use super::parse::segment_display;
use super::{AliasPolicy, PathError, PathSegment};
use crate::yaml::Mapping;
pub(super) fn navigate_path(
mut current: crate::as_yaml::YamlNode,
segments: &[PathSegment],
policy: AliasPolicy,
) -> Result<crate::as_yaml::YamlNode, PathError> {
let mut holder = None;
for segment in segments {
current = descend_one(current, holder, segment, policy)?;
holder = Some(segment);
}
Ok(current)
}
pub(super) fn resolve_for_read(
current: crate::as_yaml::YamlNode,
segment: &PathSegment,
policy: AliasPolicy,
) -> Result<crate::as_yaml::YamlNode, PathError> {
let crate::as_yaml::YamlNode::Alias(alias) = ¤t else {
return Ok(current);
};
let policy = match policy {
AliasPolicy::Expand => AliasPolicy::Follow,
policy => policy,
};
resolve_alias(alias, segment, policy)
}
pub(super) fn spelled_key(
mut keys: impl Iterator<Item = crate::as_yaml::YamlNode>,
segment: &str,
) -> Option<crate::as_yaml::YamlNode> {
keys.find(|k| k.as_scalar().is_some_and(|s| s.as_string() == segment))
}
fn lookup_own(mapping: &Mapping, key: &str) -> Option<crate::as_yaml::YamlNode> {
mapping
.get(key)
.or_else(|| mapping.get(spelled_key(mapping.keys(), key)?))
}
fn lookup_merged(
mapping: &Mapping,
key: &str,
segment: &PathSegment,
) -> Result<crate::as_yaml::YamlNode, PathError> {
use crate::anchor_resolution::{MergeError, MergedMapping};
if let Some(found) = lookup_own(mapping, key) {
return Ok(found);
}
let at = segment_display(segment);
let registry = registry_for(rowan::ast::AstNode::syntax(mapping));
let merged = MergedMapping::build(mapping.clone(), ®istry).map_err(|e| match e {
MergeError::Alias(e) => undefined_alias(e, segment),
MergeError::InvalidOperand { operand } => PathError::InvalidMergeOperand {
at: at.clone(),
operand,
},
})?;
merged
.get(key)
.or_else(|| merged.get(spelled_key(merged.keys(), key)?))
.ok_or(PathError::NotFound { at })
}
pub(super) fn descend_one(
current: crate::as_yaml::YamlNode,
holder: Option<&PathSegment>,
segment: &PathSegment,
policy: AliasPolicy,
) -> Result<crate::as_yaml::YamlNode, PathError> {
let holder = holder.unwrap_or(segment);
let current = resolve_for_read(current, holder, policy)?;
let mismatch = || PathError::TypeMismatch {
at: segment_display(holder),
};
match segment {
PathSegment::Key(key) => {
let mapping = current.mapping_content().ok_or_else(mismatch)?;
lookup_merged(&mapping, key, segment)
}
PathSegment::Index(index) => {
if let Some(seq) = current.sequence_content() {
seq.get(*index).ok_or_else(|| PathError::NotFound {
at: segment_display(segment),
})
} else if let Some(map) = current.mapping_content() {
lookup_merged(&map, &index.to_string(), segment)
} else {
Err(mismatch())
}
}
}
}