use crate::{CompileOptions, Template};
const SPEC_MD: &str = include_str!("../../../../SPEC.md");
const ROOT_README: &str = include_str!("../../../../README.md");
const CRATE_README: &str = include_str!("../../../../crates/md-tmpl/README.md");
const TS_README: &str = include_str!("../../../../crates/md-tmpl-typescript/README.md");
const PYTHON_README: &str = include_str!("../../../../crates/md-tmpl-python/README.md");
const GO_README: &str = include_str!("../../../../go/md_tmpl/README.md");
struct DocExample {
content: String,
line: usize,
file: &'static str,
}
fn extract_template_blocks(source: &str, file: &'static str) -> Vec<DocExample> {
let mut blocks = Vec::new();
let mut chars = source.char_indices().peekable();
let mut line_num = 1usize;
while let Some(&(pos, ch)) = chars.peek() {
if ch == '\n' {
line_num += 1;
chars.next();
continue;
}
if source[pos..].starts_with("```") {
let fence_line = line_num;
let after_backticks = pos + 3;
let eol = source[after_backticks..]
.find('\n')
.map_or(source.len(), |i| after_backticks + i);
for (_, c) in source[pos..=eol.min(source.len() - 1)].char_indices() {
if c == '\n' {
line_num += 1;
}
}
while let Some(&(i, _)) = chars.peek() {
if i > eol {
break;
}
chars.next();
}
let content_start = eol + 1;
if content_start >= source.len() {
continue;
}
let closing = source[content_start..].find("\n```");
if let Some(close_offset) = closing {
let content_end = content_start + close_offset;
let block = &source[content_start..content_end];
let trimmed = block.trim();
if trimmed.starts_with("---") && trimmed[3..].contains("\n---") {
if !trimmed.contains("<frontmatter>") && !trimmed.contains("<body>") {
blocks.push(DocExample {
content: trimmed.to_string(),
line: fence_line,
file,
});
}
}
let skip_to = content_end + 4; for c in source[eol + 1..skip_to.min(source.len())].chars() {
if c == '\n' {
line_num += 1;
}
}
while let Some(&(i, _)) = chars.peek() {
if i >= skip_to {
break;
}
chars.next();
}
}
} else {
chars.next();
}
}
blocks
}
fn has_empty_body(template_src: &str) -> bool {
let trimmed = template_src.trim();
if !trimmed.starts_with("---") {
return false;
}
if let Some(second_fence) = trimmed[3..].find("\n---") {
let after_fence = 3 + second_fence + 4; let body = trimmed.get(after_fence..).unwrap_or("");
body.trim().is_empty()
} else {
true
}
}
fn inject_allow_unused(template_src: &str) -> String {
let trimmed = template_src.trim();
if let Some(second_fence) = trimmed[3..].find("\n---") {
let insert_pos = 3 + second_fence;
format!(
"{}\n\nallow_unused: true{}",
&trimmed[..insert_pos],
&trimmed[insert_pos..]
)
} else {
template_src.to_string()
}
}
fn is_expected_standalone_failure(err_msg: &str) -> bool {
err_msg.contains("no value provided and no default")
|| err_msg.contains("unknown type")
|| err_msg.contains("unresolvable expression")
}
#[test]
fn all_spec_examples_compile() {
let blocks = extract_template_blocks(SPEC_MD, "SPEC.md");
assert!(
blocks.len() >= 15,
"Expected at least 15 template blocks in SPEC.md, found {}",
blocks.len()
);
let mut pass = 0;
let mut expected_skip = 0;
let mut failures = Vec::new();
for block in &blocks {
let src = if has_empty_body(&block.content) {
inject_allow_unused(&block.content)
} else {
block.content.clone()
};
let opts = CompileOptions::default().allow_unused(has_empty_body(&block.content));
match Template::compile(&src, opts) {
Ok(_) => pass += 1,
Err(e) => {
let msg = e.to_string();
if is_expected_standalone_failure(&msg) {
expected_skip += 1;
} else {
failures.push(format!(" {}:L{}: {msg}", block.file, block.line));
}
}
}
}
assert!(
failures.is_empty(),
"SPEC.md doc example compilation failures ({} of {}):\n{}",
failures.len(),
blocks.len(),
failures.join("\n")
);
eprintln!(
"SPEC.md: {pass} compiled, {expected_skip} skipped (env/import dependent), 0 failures"
);
}
#[test]
fn all_readme_examples_compile() {
let readmes: &[(&str, &str)] = &[
(ROOT_README, "README.md"),
(CRATE_README, "crates/md-tmpl/README.md"),
(TS_README, "crates/md-tmpl-typescript/README.md"),
(PYTHON_README, "crates/md-tmpl-python/README.md"),
(GO_README, "go/md_tmpl/README.md"),
];
let mut total_pass = 0;
let mut total_skip = 0;
let mut failures = Vec::new();
for &(content, file) in readmes {
let blocks = extract_template_blocks(content, file);
for block in &blocks {
let src = if has_empty_body(&block.content) {
inject_allow_unused(&block.content)
} else {
block.content.clone()
};
let opts = CompileOptions::default().allow_unused(has_empty_body(&block.content));
match Template::compile(&src, opts) {
Ok(_) => total_pass += 1,
Err(e) => {
let msg = e.to_string();
if is_expected_standalone_failure(&msg) {
total_skip += 1;
} else {
failures.push(format!(" {}:L{}: {msg}", block.file, block.line));
}
}
}
}
}
assert!(
failures.is_empty(),
"README doc example compilation failures ({} of {}):\n{}",
failures.len(),
total_pass + total_skip + failures.len(),
failures.join("\n")
);
eprintln!(
"READMEs: {total_pass} compiled, {total_skip} skipped (env/import dependent), 0 failures"
);
}
#[cfg(test)]
mod unit_tests {
use super::*;
#[test]
fn extract_finds_template_blocks() {
let md = r"Some text
```yaml
---
params:
- name = str
---
Hello {{ name }}
```
More text
";
let blocks = extract_template_blocks(md, "test.md");
assert_eq!(blocks.len(), 1);
assert!(blocks[0].content.starts_with("---"));
assert!(blocks[0].content.contains("name = str"));
}
#[test]
fn has_empty_body_detects_frontmatter_only() {
assert!(has_empty_body("---\nparams:\n - x = str\n---\n"));
assert!(has_empty_body("---\nparams:\n - x = str\n---"));
assert!(!has_empty_body(
"---\nparams:\n - x = str\n---\nHello {{ x }}"
));
}
#[test]
fn inject_allow_unused_inserts_correctly() {
let src = "---\nparams:\n - x = str\n---\n";
let injected = inject_allow_unused(src);
assert!(injected.contains("allow_unused: true"));
assert!(injected.contains("params:"));
}
}