use granit_parser::{Parser, ScanError};
const README: &str = include_str!("../README.md");
fn minimal_example_section() -> &'static str {
README
.split_once("## Minimal example")
.and_then(|(_, tail)| tail.split_once("\n## ").map(|(section, _)| section))
.expect("README must contain a '## Minimal example' section before the next heading")
}
fn extract_yaml_input(section: &str) -> &str {
section
.split_once("let yaml = r#\"")
.and_then(|(_, tail)| tail.split_once("\"#;").map(|(yaml, _)| yaml))
.expect("README minimal example must contain a raw string YAML input")
}
fn extract_expected_output(section: &str) -> &str {
section
.split_once("```text\n")
.and_then(|(_, tail)| tail.split_once("\n```").map(|(text, _)| text))
.expect("README minimal example must contain a fenced text block with expected output")
}
fn render_readme_example(yaml: &str) -> Result<String, ScanError> {
let mut lines = Vec::new();
for next in Parser::new_from_str(yaml) {
let (event, span) = next?;
if let Some(tag) = event.tag() {
if let Some((value, _style)) = event.scalar() {
lines.push(format!(
"scalar tag: {tag} core-str={} for {value:?}",
tag.is_yaml_core_schema_tag("str")
));
} else if event.is_node() {
lines.push(format!("node tag: {tag} custom={}", tag.is_custom()));
}
}
lines.push(format!(
"{event:?} bytes={:?} source={:?}",
span.byte_range(),
span.slice(yaml)
));
}
Ok(lines.join("\n"))
}
#[test]
fn minimal_example_output_matches_readme() {
let section = minimal_example_section();
let yaml = extract_yaml_input(section);
let expected = extract_expected_output(section);
let actual =
render_readme_example(yaml).expect("README example YAML should parse successfully");
println!("Actual output:\n{actual}");
println!("Expected output:\n{expected}");
assert_eq!(actual, expected, "README example output is out of sync");
}