use crate::error::{Error, Result};
use crate::parser::Parser;
use crate::prelude::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CommentKind {
Line,
Inline,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Comment {
pub text: String,
pub start: usize,
pub end: usize,
pub kind: CommentKind,
}
pub fn load_comments(input: &str) -> Result<Vec<Comment>> {
let limits = crate::ParserConfig::default();
if input.len() > limits.max_document_length {
return Err(Error::Parse(format!(
"document exceeds maximum length of {} bytes",
limits.max_document_length
)));
}
let mut parser = Parser::with_max_events(input, limits.max_events);
parser.enable_comment_capture();
loop {
match parser.next_event() {
Ok(ev) => {
if matches!(ev, crate::parser::Event::StreamEnd) {
break;
}
}
Err(e) => return Err(e.into_error(input, limits.max_events)),
}
}
Ok(parser.take_comments())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_oversized_input_is_refused_like_a_value_parse() {
let limit = crate::ParserConfig::default().max_document_length;
let input = format!("# c\n{}", "a".repeat(limit));
let err = load_comments(&input).unwrap_err().to_string();
assert!(err.contains("maximum length"), "{err}");
}
#[test]
fn empty_document_has_no_comments() {
assert_eq!(load_comments("").unwrap(), vec![]);
assert_eq!(load_comments("k: 1\n").unwrap(), vec![]);
}
#[test]
fn single_line_comment() {
let cs = load_comments("# hello\n").unwrap();
assert_eq!(cs.len(), 1);
assert_eq!(cs[0].text, " hello");
assert_eq!(cs[0].kind, CommentKind::Line);
assert_eq!(cs[0].start, 0);
assert_eq!(cs[0].end, 7);
}
#[test]
fn inline_comment_classification() {
let cs = load_comments("k: 1 # trailing\n").unwrap();
assert_eq!(cs.len(), 1);
assert_eq!(cs[0].kind, CommentKind::Inline);
assert!(cs[0].text.contains("trailing"));
}
#[test]
fn mixed_leading_and_inline() {
let yaml = "# top\nk: 1 # tail\n# bottom\n";
let cs = load_comments(yaml).unwrap();
assert_eq!(cs.len(), 3);
assert_eq!(cs[0].kind, CommentKind::Line);
assert_eq!(cs[1].kind, CommentKind::Inline);
assert_eq!(cs[2].kind, CommentKind::Line);
}
#[test]
fn indented_line_comment_is_still_line() {
let cs = load_comments("k:\n # indented\n v: 1\n").unwrap();
assert!(!cs.is_empty(), "expected a non-empty value");
assert_eq!(cs[0].kind, CommentKind::Line);
}
#[test]
fn source_order_preserved() {
let yaml = "# a\n# b\n# c\n";
let cs = load_comments(yaml).unwrap();
assert_eq!(cs.len(), 3);
assert!(cs[0].start < cs[1].start);
assert!(cs[1].start < cs[2].start);
}
}