use std::borrow::Cow;
use crate::pos::Span;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EventMeta<'input> {
pub anchor: Option<&'input str>,
pub anchor_loc: Option<Span>,
pub tag: Option<Cow<'input, str>>,
pub tag_loc: Option<Span>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Chomp {
Strip,
Clip,
Keep,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CollectionStyle {
Block,
Flow,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScalarStyle {
Plain,
SingleQuoted,
DoubleQuoted,
Literal(Chomp),
Folded(Chomp),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Event<'input> {
StreamStart,
StreamEnd,
Comment {
text: &'input str,
},
Alias {
name: &'input str,
},
DocumentStart {
explicit: bool,
version: Option<(u8, u8)>,
tag_directives: Vec<(String, String)>,
},
DocumentEnd {
explicit: bool,
},
SequenceStart {
style: CollectionStyle,
meta: Option<Box<EventMeta<'input>>>,
},
SequenceEnd,
MappingStart {
style: CollectionStyle,
meta: Option<Box<EventMeta<'input>>>,
},
MappingEnd,
Scalar {
value: Cow<'input, str>,
style: ScalarStyle,
meta: Option<Box<EventMeta<'input>>>,
},
}
impl Event<'_> {
#[must_use]
#[inline]
pub fn anchor(&self) -> Option<&str> {
match self {
Self::Scalar { meta, .. }
| Self::SequenceStart { meta, .. }
| Self::MappingStart { meta, .. } => meta.as_ref().and_then(|m| m.anchor),
Self::StreamStart
| Self::StreamEnd
| Self::Comment { .. }
| Self::Alias { .. }
| Self::DocumentStart { .. }
| Self::DocumentEnd { .. }
| Self::SequenceEnd
| Self::MappingEnd => None,
}
}
#[must_use]
#[inline]
pub fn anchor_loc(&self) -> Option<Span> {
match self {
Self::Scalar { meta, .. }
| Self::SequenceStart { meta, .. }
| Self::MappingStart { meta, .. } => meta.as_ref().and_then(|m| m.anchor_loc),
Self::StreamStart
| Self::StreamEnd
| Self::Comment { .. }
| Self::Alias { .. }
| Self::DocumentStart { .. }
| Self::DocumentEnd { .. }
| Self::SequenceEnd
| Self::MappingEnd => None,
}
}
#[must_use]
#[inline]
pub fn tag(&self) -> Option<&str> {
match self {
Self::Scalar { meta, .. }
| Self::SequenceStart { meta, .. }
| Self::MappingStart { meta, .. } => meta.as_ref().and_then(|m| m.tag.as_deref()),
Self::StreamStart
| Self::StreamEnd
| Self::Comment { .. }
| Self::Alias { .. }
| Self::DocumentStart { .. }
| Self::DocumentEnd { .. }
| Self::SequenceEnd
| Self::MappingEnd => None,
}
}
#[must_use]
#[inline]
pub fn tag_loc(&self) -> Option<Span> {
match self {
Self::Scalar { meta, .. }
| Self::SequenceStart { meta, .. }
| Self::MappingStart { meta, .. } => meta.as_ref().and_then(|m| m.tag_loc),
Self::StreamStart
| Self::StreamEnd
| Self::Comment { .. }
| Self::Alias { .. }
| Self::DocumentStart { .. }
| Self::DocumentEnd { .. }
| Self::SequenceEnd
| Self::MappingEnd => None,
}
}
}
#[expect(
clippy::redundant_pub_crate,
reason = "pub(crate) inside private module — accessibility requires crate-wide visibility"
)]
#[inline]
pub(crate) fn make_meta<'input>(
anchor: Option<&'input str>,
anchor_loc: Option<Span>,
tag: Option<Cow<'input, str>>,
tag_loc: Option<Span>,
) -> Option<Box<EventMeta<'input>>> {
if anchor.is_none() && tag.is_none() {
None
} else {
Some(Box::new(EventMeta {
anchor,
anchor_loc,
tag,
tag_loc,
}))
}
}
const _: () = assert!(
std::mem::size_of::<Event<'_>>() <= 56,
"Event must be at most 56 bytes after EventMeta boxing"
);
#[cfg(test)]
#[expect(clippy::unwrap_used, reason = "test code")]
mod tests {
use std::borrow::Cow;
use super::*;
use crate::pos::Span;
const SPAN: Span = Span { start: 0, end: 4 };
#[test]
fn make_meta_returns_none_when_all_fields_absent() {
let meta = make_meta(None, None, None, None);
assert!(
meta.is_none(),
"make_meta must return None when anchor and tag are both None"
);
}
#[test]
fn make_meta_returns_some_when_anchor_only() {
let meta = make_meta(Some("a"), Some(SPAN), None, None).unwrap();
assert_eq!(meta.anchor, Some("a"));
assert_eq!(meta.anchor_loc, Some(SPAN));
assert!(meta.tag.is_none());
assert!(meta.tag_loc.is_none());
}
#[test]
fn make_meta_returns_some_when_tag_only() {
let meta = make_meta(None, None, Some(Cow::Borrowed("!str")), Some(SPAN)).unwrap();
assert!(meta.anchor.is_none());
assert!(meta.anchor_loc.is_none());
assert_eq!(meta.tag.as_deref(), Some("!str"));
assert_eq!(meta.tag_loc, Some(SPAN));
}
#[test]
fn make_meta_returns_some_when_both_anchor_and_tag() {
let meta = make_meta(
Some("a"),
Some(SPAN),
Some(Cow::Borrowed("!str")),
Some(SPAN),
)
.unwrap();
assert_eq!(meta.anchor, Some("a"));
assert_eq!(meta.tag.as_deref(), Some("!str"));
}
#[test]
fn event_size_at_most_56_bytes() {
assert!(
std::mem::size_of::<Event<'_>>() <= 56,
"Event size {} exceeds 56 bytes",
std::mem::size_of::<Event<'_>>()
);
}
}