use std::borrow::Cow;
#[cfg(feature = "properties")]
use std::collections::HashMap;
use std::mem;
#[cfg(feature = "properties")]
use std::rc::Rc;
use granit_parser::ScalarStyle;
use super::error::Error;
use super::events::{Ev, Events, ReplayEvents};
use super::options::MergeKeyPolicy;
use super::tags::SfTag;
use crate::location::Location;
use crate::parse_scalars::scalar_is_nullish;
pub(super) fn simple_tagged_enum_name(
raw_tag: &Option<Cow<'_, str>>,
tag: &SfTag,
) -> Option<String> {
if !matches!(tag, SfTag::Other) {
return None;
}
let raw = raw_tag.as_deref()?;
let mut candidate =
if let Some(inner) = raw.strip_prefix("!<").and_then(|s| s.strip_suffix('>')) {
inner
} else {
raw
};
if let Some(stripped) = candidate.strip_prefix("tag:yaml.org,2002:") {
candidate = stripped;
}
candidate = candidate.trim_start_matches('!');
if candidate.is_empty() || candidate.contains([':', '!']) {
return None;
}
Some(candidate.to_owned())
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub(super) enum KeyFingerprint {
Scalar { value: String, tag: SfTag },
Sequence(Vec<KeyFingerprint>),
Mapping(Vec<(KeyFingerprint, KeyFingerprint)>),
#[default]
Default,
}
pub(super) fn canonical_scalar_key_tag(tag: SfTag) -> SfTag {
if tag.can_parse_into_string() || tag == SfTag::NonSpecific {
SfTag::String
} else {
tag
}
}
impl KeyFingerprint {
pub(super) fn stringy_scalar_value(&self) -> Option<&str> {
match self {
KeyFingerprint::Scalar { value, tag } => {
if tag.can_parse_into_string() && tag != &SfTag::Binary {
Some(value.as_str())
} else {
None
}
}
_ => None,
}
}
}
pub(super) enum KeyNode<'a> {
Fingerprinted {
fingerprint: KeyFingerprint,
events: Vec<Ev<'a>>,
location: Location,
},
Scalar {
events: Vec<Ev<'a>>,
location: Location,
},
}
impl<'a> KeyNode<'a> {
pub(super) fn fingerprint(&self) -> Cow<'_, KeyFingerprint> {
match self {
KeyNode::Fingerprinted { fingerprint, .. } => Cow::Borrowed(fingerprint),
KeyNode::Scalar { events, .. } => {
if let Some(Ev::Scalar { tag, value, .. }) = events.first() {
Cow::Owned(KeyFingerprint::Scalar {
tag: canonical_scalar_key_tag(*tag),
value: value.to_string(),
})
} else {
unreachable!()
}
}
}
}
pub(super) fn events(&self) -> &[Ev<'a>] {
match self {
KeyNode::Fingerprinted { events, .. } => events,
KeyNode::Scalar { events, .. } => events,
}
}
pub(super) fn take_events(&mut self) -> Vec<Ev<'a>> {
match self {
KeyNode::Fingerprinted { events, .. } => mem::take(events),
KeyNode::Scalar { events, .. } => mem::take(events),
}
}
pub(super) fn take_fingerprint(&mut self) -> KeyFingerprint {
match self {
KeyNode::Fingerprinted { fingerprint, .. } => mem::take(fingerprint),
KeyNode::Scalar { .. } => self.fingerprint().into_owned(),
}
}
pub(super) fn location(&self) -> Location {
let location = match self {
KeyNode::Fingerprinted { location, .. } => location,
KeyNode::Scalar { location, .. } => location,
};
*location
}
}
pub(super) struct PendingEntry<'a> {
pub(super) key: KeyNode<'a>,
pub(super) value: KeyNode<'a>,
pub(super) reference_location: Location,
pub(super) field_comments: Vec<String>,
pub(super) value_separator_comments: Vec<String>,
pub(super) value_comments: Vec<String>,
}
pub(super) fn one_entry_map_spans<'a>(events: &[Ev<'a>]) -> Option<(usize, usize, usize, usize)> {
if events.len() < 4 {
return None;
}
match events.first()? {
Ev::MapStart { .. } => {}
_ => return None,
}
match events.last()? {
Ev::MapEnd { .. } => {}
_ => return None,
}
let mut i = 1; let key_start = i;
i += skip_one_node_len(events, i)?;
let key_end = i;
let val_start = i;
i += skip_one_node_len(events, i)?;
let val_end = i;
if i != events.len() - 1 {
return None;
}
Some((key_start, key_end, val_start, val_end))
}
pub(super) fn skip_one_node_len<'a>(events: &[Ev<'a>], mut i: usize) -> Option<usize> {
match events.get(i)? {
Ev::Scalar { .. } => Some(1),
Ev::SeqStart { .. } => {
let start = i;
let mut depth = 1i32;
i += 1;
while i < events.len() {
match events.get(i)? {
Ev::SeqStart { .. } => depth += 1,
Ev::SeqEnd { .. } => {
depth -= 1;
if depth == 0 {
return Some(i - start + 1);
}
}
Ev::MapStart { .. } => depth += 1,
Ev::MapEnd { .. } => {
depth -= 1;
}
Ev::Scalar { .. } => {}
Ev::Taken { .. } => return None,
}
i += 1;
}
None
}
Ev::MapStart { .. } => {
let start = i;
let mut depth = 1i32;
i += 1;
while i < events.len() {
match events.get(i)? {
Ev::MapStart { .. } => depth += 1,
Ev::MapEnd { .. } => {
depth -= 1;
if depth == 0 {
return Some(i - start + 1);
}
}
Ev::SeqStart { .. } => depth += 1,
Ev::SeqEnd { .. } => {
depth -= 1;
}
Ev::Scalar { .. } => {}
Ev::Taken { .. } => return None,
}
i += 1;
}
None
}
Ev::SeqEnd { .. } | Ev::MapEnd { .. } => None,
Ev::Taken { .. } => None,
}
}
pub(super) fn capture_node<'a>(ev: &mut dyn Events<'a>) -> Result<KeyNode<'a>, Error> {
let Some(event) = ev.next()? else {
return Err(Error::eof().with_location(ev.last_location()));
};
match event {
Ev::Scalar {
value,
tag,
raw_tag,
style,
anchor,
location,
} => {
let scalar_ev = Ev::Scalar {
value,
tag,
raw_tag,
style,
anchor,
location,
};
Ok(KeyNode::Scalar {
events: vec![scalar_ev],
location,
})
}
Ev::SeqStart {
anchor,
tag,
raw_tag,
location,
} => {
let mut events = vec![Ev::SeqStart {
anchor,
tag,
raw_tag,
location,
}];
let mut elements = Vec::new();
loop {
match ev.peek()? {
Some(Ev::SeqEnd { location: end_loc }) => {
let end_loc = *end_loc;
let _ = ev.next()?;
events.push(Ev::SeqEnd { location: end_loc });
break;
}
Some(_) => {
let mut child = capture_node(ev)?; let fp = child.take_fingerprint();
let child_events = child.take_events();
elements.push(fp);
events.reserve(child_events.len());
events.extend(child_events);
}
None => {
return Err(Error::eof().with_location(ev.last_location()));
}
}
}
Ok(KeyNode::Fingerprinted {
fingerprint: KeyFingerprint::Sequence(elements),
events,
location,
})
}
Ev::MapStart { anchor, location } => {
let mut events = vec![Ev::MapStart { anchor, location }];
let mut entries = Vec::new();
loop {
match ev.peek()? {
Some(Ev::MapEnd { location: end_loc }) => {
let end_loc = *end_loc;
let _ = ev.next()?;
events.push(Ev::MapEnd { location: end_loc });
break;
}
Some(_) => {
let mut key = capture_node(ev)?; let key_fp = key.take_fingerprint();
let mut value = capture_node(ev)?; let value_fp = value.take_fingerprint();
entries.push((key_fp, value_fp));
let key_events = key.take_events();
let value_events = value.take_events();
events.reserve(key_events.len() + value_events.len());
events.extend(key_events);
events.extend(value_events);
}
None => {
return Err(Error::eof().with_location(ev.last_location()));
}
}
}
Ok(KeyNode::Fingerprinted {
fingerprint: KeyFingerprint::Mapping(entries),
events,
location,
})
}
Ev::SeqEnd { location } | Ev::MapEnd { location } => {
Err(Error::UnexpectedContainerEndWhileReadingKeyNode { location })
}
Ev::Taken { location } => Err(Error::unexpected("consumed event").with_location(location)),
}
}
pub(super) fn simple_tagged_node_name(event: &Ev<'_>) -> Option<(String, Location)> {
match event {
Ev::Scalar {
tag,
raw_tag,
location,
..
}
| Ev::SeqStart {
tag,
raw_tag,
location,
..
} => simple_tagged_enum_name(raw_tag, tag).map(|name| (name, *location)),
_ => None,
}
}
pub(super) fn strip_root_tag_for_externally_tagged_payload<'a>(events: &mut [Ev<'a>]) {
match events.first_mut() {
Some(Ev::Scalar { tag, raw_tag, .. }) => {
*tag = SfTag::None;
*raw_tag = None;
}
Some(Ev::SeqStart { tag, raw_tag, .. }) => {
*tag = SfTag::None;
*raw_tag = None;
}
_ => {}
}
}
pub(super) fn externally_tagged_payload_as_map_events<'a>(
variant: String,
tag_location: Location,
mut payload_events: Vec<Ev<'a>>,
) -> Vec<Ev<'a>> {
let end_location = payload_events
.last()
.map(Ev::location)
.unwrap_or(tag_location);
let mut events = Vec::with_capacity(payload_events.len() + 3);
events.push(Ev::MapStart {
anchor: 0,
location: tag_location,
});
events.push(Ev::Scalar {
value: Cow::Owned(variant),
tag: SfTag::String,
raw_tag: None,
style: ScalarStyle::Plain,
anchor: 0,
location: tag_location,
});
events.append(&mut payload_events);
events.push(Ev::MapEnd {
location: end_location,
});
events
}
pub(super) fn capture_simple_tagged_node_as_map_events<'a>(
ev: &mut dyn Events<'a>,
) -> Result<Option<Vec<Ev<'a>>>, Error> {
let Some((variant, tag_location)) = ev.peek()?.and_then(|event| simple_tagged_node_name(event))
else {
return Ok(None);
};
let mut payload_node = capture_node(ev)?;
let mut payload_events = payload_node.take_events();
strip_root_tag_for_externally_tagged_payload(&mut payload_events);
Ok(Some(externally_tagged_payload_as_map_events(
variant,
tag_location,
payload_events,
)))
}
#[inline]
pub(super) fn is_merge_key(node: &KeyNode) -> bool {
let events = node.events();
if events.len() != 1 {
return false;
}
events.first().is_some_and(is_merge_key_event)
}
#[inline]
pub(super) fn is_merge_key_event(event: &Ev<'_>) -> bool {
matches!(
event,
Ev::Scalar {
value,
tag,
style: ScalarStyle::Plain,
..
} if tag == &SfTag::None && value.as_ref() == "<<"
)
}
pub(super) fn validate_no_merge_keys_in_node_events(events: &[Ev<'_>]) -> Result<(), Error> {
fn eof_location(events: &[Ev<'_>]) -> Location {
events.last().map(Ev::location).unwrap_or(Location::UNKNOWN)
}
fn visit_node(events: &[Ev<'_>], mut index: usize) -> Result<usize, Error> {
match events.get(index) {
Some(Ev::Scalar { .. }) => Ok(index + 1),
Some(Ev::SeqStart { .. }) => {
index += 1;
loop {
match events.get(index) {
Some(Ev::SeqEnd { .. }) => return Ok(index + 1),
Some(Ev::MapEnd { location }) => {
return Err(Error::UnexpectedContainerEndWhileSkippingNode {
location: *location,
});
}
Some(_) => index = visit_node(events, index)?,
None => return Err(Error::eof().with_location(eof_location(events))),
}
}
}
Some(Ev::MapStart { .. }) => {
index += 1;
loop {
match events.get(index) {
Some(Ev::MapEnd { .. }) => return Ok(index + 1),
Some(Ev::SeqEnd { location }) => {
return Err(Error::UnexpectedContainerEndWhileSkippingNode {
location: *location,
});
}
Some(event) => {
if is_merge_key_event(event) {
return Err(Error::MergeKeyNotAllowed {
location: event.location(),
});
}
index = visit_node(events, index)?;
index = visit_node(events, index)?;
}
None => return Err(Error::eof().with_location(eof_location(events))),
}
}
}
Some(Ev::SeqEnd { location } | Ev::MapEnd { location }) => {
Err(Error::UnexpectedContainerEndWhileSkippingNode {
location: *location,
})
}
Some(Ev::Taken { location }) => {
Err(Error::unexpected("consumed event").with_location(*location))
}
None => Err(Error::eof().with_location(eof_location(events))),
}
}
let next = visit_node(events, 0)?;
if next == events.len() {
Ok(())
} else {
Err(Error::unexpected("single YAML node").with_location(events[next].location()))
}
}
pub(super) fn pending_entries_from_events<'a>(
events: Vec<Ev<'a>>,
location: Location,
reference_location: Location,
merge_keys: MergeKeyPolicy,
#[cfg(feature = "properties")] property_map: Option<Rc<HashMap<String, String>>>,
) -> Result<Vec<PendingEntry<'a>>, Error> {
let mut replay = ReplayEvents::with_reference(
events,
reference_location,
#[cfg(feature = "properties")]
property_map.clone(),
);
match replay.peek()? {
Some(Ev::Scalar { value, style, .. }) if scalar_is_nullish(value.as_ref(), style) => {
Ok(Vec::new())
}
Some(Ev::Scalar { location, .. }) => Err(Error::MergeValueNotMapOrSeqOfMaps {
location: *location,
}),
Some(Ev::MapStart { .. }) => {
collect_entries_from_map(&mut replay, reference_location, merge_keys)
}
Some(Ev::SeqStart { .. }) => {
let mut batches = Vec::new();
let _ = replay.next()?; loop {
match replay.peek()? {
Some(Ev::SeqEnd { .. }) => {
let _ = replay.next()?;
break;
}
Some(_) => {
let _ = replay.peek()?;
let element_ref_loc = replay.reference_location();
let mut element = capture_node(&mut replay)?;
batches.push(pending_entries_from_events(
element.take_events(),
element.location(),
element_ref_loc,
merge_keys,
#[cfg(feature = "properties")]
property_map.clone(),
)?); }
None => {
return Err(Error::eof().with_location(replay.last_location()));
}
}
}
let mut merged = Vec::new();
while let Some(mut nested) = batches.pop() {
merged.append(&mut nested);
}
Ok(merged)
}
Some(other) => Err(Error::MergeValueNotMapOrSeqOfMaps {
location: other.location(),
}),
None => Err(Error::eof().with_location(location)),
}
}
pub(super) fn pending_entries_from_live_events<'a>(
ev: &mut dyn Events<'a>,
merge_reference_location: Location,
merge_keys: MergeKeyPolicy,
) -> Result<Vec<PendingEntry<'a>>, Error> {
#[cfg(feature = "properties")]
let property_map = ev.property_map().map(Rc::clone);
match ev.peek()? {
Some(Ev::Scalar { value, style, .. }) if scalar_is_nullish(value.as_ref(), style) => {
let _ = ev.next()?;
Ok(Vec::new())
}
Some(Ev::Scalar { location, .. }) => Err(Error::MergeValueNotMapOrSeqOfMaps {
location: *location,
}),
Some(Ev::MapStart { .. }) => {
let mut node = capture_node(ev)?;
pending_entries_from_events(
node.take_events(),
node.location(),
merge_reference_location,
merge_keys,
#[cfg(feature = "properties")]
property_map,
)
}
Some(Ev::SeqStart { .. }) => {
let _ = ev.next()?; let mut batches = Vec::new();
loop {
match ev.peek()? {
Some(Ev::SeqEnd { .. }) => {
let _ = ev.next()?;
break;
}
Some(_) => {
let _ = ev.peek()?;
let element_ref_loc = ev.reference_location();
let mut element = capture_node(ev)?;
batches.push(pending_entries_from_events(
element.take_events(),
element.location(),
element_ref_loc,
merge_keys,
#[cfg(feature = "properties")]
property_map.clone(),
)?);
}
None => return Err(Error::eof().with_location(ev.last_location())),
}
}
let mut merged = Vec::new();
while let Some(mut nested) = batches.pop() {
merged.append(&mut nested);
}
Ok(merged)
}
Some(other) => Err(Error::MergeValueNotMapOrSeqOfMaps {
location: other.location(),
}),
None => Err(Error::eof().with_location(ev.last_location())),
}
}
pub(super) fn collect_entries_from_map<'a>(
ev: &mut dyn Events<'a>,
reference_location: Location,
merge_keys: MergeKeyPolicy,
) -> Result<Vec<PendingEntry<'a>>, Error> {
let Some(Ev::MapStart { .. }) = ev.next()? else {
return Err(Error::MergeValueNotMapOrSeqOfMaps {
location: ev.last_location(),
});
};
let mut fields = Vec::new();
let mut merges = Vec::new();
loop {
match ev.peek()? {
Some(Ev::MapEnd { .. }) => {
let _ = ev.next()?;
break;
}
Some(_) => {
let key_comments = ev.take_leading_comments_for_next_node()?;
let key = capture_node(ev)?;
if is_merge_key(&key) {
match merge_keys {
MergeKeyPolicy::Merge => {
let _ = ev.peek()?;
let merge_ref_loc = ev.reference_location();
merges.push(pending_entries_from_live_events(
ev,
merge_ref_loc,
merge_keys,
)?);
continue;
}
MergeKeyPolicy::AsOrdinary => {}
MergeKeyPolicy::Error => {
return Err(Error::MergeKeyNotAllowed {
location: key.location(),
});
}
}
}
let field_comments = key_comments;
let value_separator_comments = ev.take_separator_comments_before_mapping_value()?;
let value_comments = ev.take_leading_comments_for_next_node()?;
let value = capture_node(ev)?;
fields.push(PendingEntry {
key,
value,
reference_location,
field_comments,
value_separator_comments,
value_comments,
});
}
None => {
return Err(Error::eof().with_location(ev.last_location()));
}
}
}
let mut entries = fields;
while let Some(mut nested) = merges.pop() {
entries.append(&mut nested);
}
Ok(entries)
}