use crate::event::ScalarStyle;
use crate::pos::Span;
#[derive(Debug, Clone, PartialEq)]
pub struct Document<Loc = Span> {
pub root: Node<Loc>,
pub version: Option<(u8, u8)>,
pub tags: Vec<(String, String)>,
pub comments: Vec<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Node<Loc = Span> {
Scalar {
value: String,
style: ScalarStyle,
anchor: Option<String>,
tag: Option<String>,
loc: Loc,
leading_comments: Vec<String>,
trailing_comment: Option<String>,
},
Mapping {
entries: Vec<(Self, Self)>,
anchor: Option<String>,
tag: Option<String>,
loc: Loc,
leading_comments: Vec<String>,
trailing_comment: Option<String>,
},
Sequence {
items: Vec<Self>,
anchor: Option<String>,
tag: Option<String>,
loc: Loc,
leading_comments: Vec<String>,
trailing_comment: Option<String>,
},
Alias {
name: String,
loc: Loc,
leading_comments: Vec<String>,
trailing_comment: Option<String>,
},
}
impl<Loc> Node<Loc> {
pub fn anchor(&self) -> Option<&str> {
match self {
Self::Scalar { anchor, .. }
| Self::Mapping { anchor, .. }
| Self::Sequence { anchor, .. } => anchor.as_deref(),
Self::Alias { .. } => None,
}
}
pub fn leading_comments(&self) -> &[String] {
match self {
Self::Scalar {
leading_comments, ..
}
| Self::Mapping {
leading_comments, ..
}
| Self::Sequence {
leading_comments, ..
}
| Self::Alias {
leading_comments, ..
} => leading_comments,
}
}
pub fn trailing_comment(&self) -> Option<&str> {
match self {
Self::Scalar {
trailing_comment, ..
}
| Self::Mapping {
trailing_comment, ..
}
| Self::Sequence {
trailing_comment, ..
}
| Self::Alias {
trailing_comment, ..
} => trailing_comment.as_deref(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::event::ScalarStyle;
use crate::pos::{Pos, Span};
fn zero_span() -> Span {
Span {
start: Pos::ORIGIN,
end: Pos::ORIGIN,
}
}
fn plain_scalar(value: &str) -> Node<Span> {
Node::Scalar {
value: value.to_owned(),
style: ScalarStyle::Plain,
anchor: None,
tag: None,
loc: zero_span(),
leading_comments: Vec::new(),
trailing_comment: None,
}
}
#[test]
fn node_debug_includes_leading_comments() {
let node = Node::Scalar {
value: "val".to_owned(),
style: ScalarStyle::Plain,
anchor: None,
tag: None,
loc: zero_span(),
leading_comments: vec!["# note".to_owned()],
trailing_comment: None,
};
let debug = format!("{node:?}");
assert!(debug.contains("# note"), "debug output: {debug}");
}
#[test]
fn node_partial_eq_considers_leading_comments() {
let a = Node::Scalar {
value: "val".to_owned(),
style: ScalarStyle::Plain,
anchor: None,
tag: None,
loc: zero_span(),
leading_comments: vec!["# a".to_owned()],
trailing_comment: None,
};
let b = Node::Scalar {
value: "val".to_owned(),
style: ScalarStyle::Plain,
anchor: None,
tag: None,
loc: zero_span(),
leading_comments: vec!["# b".to_owned()],
trailing_comment: None,
};
assert_ne!(a, b);
}
#[test]
fn node_clone_preserves_comments() {
let node = Node::Scalar {
value: "val".to_owned(),
style: ScalarStyle::Plain,
anchor: None,
tag: None,
loc: zero_span(),
leading_comments: vec!["# x".to_owned()],
trailing_comment: Some("# y".to_owned()),
};
let cloned = node.clone();
assert_eq!(node, cloned);
assert_eq!(cloned.leading_comments(), &["# x"]);
assert_eq!(cloned.trailing_comment(), Some("# y"));
}
#[test]
fn plain_scalar_has_empty_comments() {
let n = plain_scalar("hello");
assert!(n.leading_comments().is_empty());
assert!(n.trailing_comment().is_none());
}
}