use std::fs;
use std::path::{Path, PathBuf};
fn root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
fn read(rel: &str) -> String {
fs::read_to_string(root().join(rel)).unwrap_or_else(|e| panic!("cannot read {rel}: {e}"))
}
fn count_tests_in(dir: &Path) -> usize {
let mut count = 0;
let mut stack = vec![dir.to_path_buf()];
while let Some(d) = stack.pop() {
for entry in fs::read_dir(&d).unwrap() {
let p = entry.unwrap().path();
if p.is_dir() {
stack.push(p);
} else if p.extension().is_some_and(|x| x == "rs") {
if p.ends_with("tests/docs_sync.rs") {
continue;
}
count += fs::read_to_string(&p)
.unwrap()
.lines()
.filter(|l| l.trim_start().starts_with("#[test]"))
.count();
}
}
}
count
}
fn leading_int(text: &str, marker: &str) -> usize {
let idx = text
.find(marker)
.unwrap_or_else(|| panic!("doc missing marker '{marker}'"));
let digits: String = text[..idx]
.chars()
.rev()
.skip_while(|c| c.is_whitespace())
.take_while(|c| c.is_ascii_digit())
.collect();
digits
.chars()
.rev()
.collect::<String>()
.parse()
.unwrap_or_else(|_| panic!("no integer before '{marker}'"))
}
#[test]
fn agents_md_structure_lists_every_module() {
let lib = read("src/lib.rs");
let agents = read("AGENTS.md");
let missing: Vec<String> = lib
.lines()
.filter_map(|l| {
let t = l.trim();
t.strip_prefix("pub mod ")?
.strip_suffix(';')
.map(str::to_owned)
})
.filter(|m| !agents.contains(m))
.collect();
assert!(
missing.is_empty(),
"modules missing from the AGENTS.md project structure: {missing:?}"
);
}
#[test]
fn architecture_md_mentions_every_module() {
let lib = read("src/lib.rs");
let arch = read("ARCHITECTURE.md");
let missing: Vec<String> = lib
.lines()
.filter_map(|l| {
let t = l.trim();
t.strip_prefix("pub mod ")?
.strip_suffix(';')
.map(str::to_owned)
})
.filter(|m| !arch.contains(m) && !arch.contains(&format!("{m}::")))
.collect();
assert!(
missing.is_empty(),
"modules missing from ARCHITECTURE.md: {missing:?}"
);
}
#[test]
fn todo_md_inline_test_count_matches_code() {
let actual = count_tests_in(&root().join("src"));
let claimed = leading_int(&read("TODO.md"), "inline /");
assert_eq!(
claimed, actual,
"TODO.md inline-test count is stale (run `cargo test`, then update the header)"
);
}
#[test]
fn todo_md_integration_test_count_matches_code() {
let actual = count_tests_in(&root().join("tests"));
let claimed = leading_int(&read("TODO.md"), "integration =");
assert_eq!(
claimed, actual,
"TODO.md integration-test count is stale (run `cargo test`, then update the header)"
);
}
fn lib_version() -> String {
let lib = read("src/lib.rs");
let marker = "pub const VERSION: &str = \"";
let start = lib
.find(marker)
.unwrap_or_else(|| panic!("src/lib.rs missing `pub const VERSION`"))
+ marker.len();
let rest = &lib[start..];
let end = rest
.find('"')
.unwrap_or_else(|| panic!("src/lib.rs VERSION literal not terminated"));
rest[..end].to_owned()
}
fn readme_install_versions() -> Vec<String> {
let readme = read("README.md");
let mut out = Vec::new();
let mut rest = readme.as_str();
while let Some(idx) = rest.find("ppt-rs = ") {
let after = &rest[idx + "ppt-rs = ".len()..];
let after = after.trim_start();
if let Some(stripped) = after.strip_prefix('"')
&& let Some(end) = stripped.find('"')
{
out.push(stripped[..end].to_owned());
rest = &rest[idx + "ppt-rs = ".len() + (after.len() - stripped.len()) + end + 1..];
continue;
}
if let Some(stripped) = after.strip_prefix('{')
&& let Some(v_idx) = stripped.find("version = \"")
{
let v_after = &stripped[v_idx + "version = \"".len()..];
if let Some(end) = v_after.find('"') {
out.push(v_after[..end].to_owned());
rest = &rest[idx + "ppt-rs = ".len() + (after.len() - stripped.len()) + end + 1..];
continue;
}
}
rest = &rest[idx + "ppt-rs = ".len()..];
}
out
}
#[test]
fn readme_install_versions_match_lib_version() {
let actual = lib_version();
let claimed = readme_install_versions();
assert!(
!claimed.is_empty(),
"README.md does not document an install version (expected `ppt-rs = \"{actual}\"`)"
);
assert!(
claimed.iter().all(|v| v == &actual),
"README install snippet stale: lib = {actual}, README mentions = {claimed:?}"
);
}
fn mcp_tool_count() -> usize {
let mcp = read("src/mcp/mod.rs");
let mut count = 0;
let mut attr_depth: i32 = 0; let mut pending_tool = false; for raw in mcp.lines() {
let line = raw.trim_start();
if attr_depth > 0 {
for ch in line.chars() {
match ch {
'[' => attr_depth += 1,
']' => attr_depth -= 1,
_ => {}
}
}
continue;
}
if line.starts_with("#[tool") {
pending_tool = true;
for ch in line.chars() {
match ch {
'[' => attr_depth += 1,
']' => attr_depth -= 1,
_ => {}
}
}
continue;
}
if line.starts_with('#') {
pending_tool = false;
continue;
}
if pending_tool && line.starts_with("fn ") && line.contains('(') && !line.contains("::") {
count += 1;
pending_tool = false;
continue;
}
if !line.is_empty() && !line.starts_with("//") {
pending_tool = false;
}
}
count
}
#[test]
fn mcp_tools_are_documented_consistently() {
let actual = mcp_tool_count();
assert!(
actual > 0,
"could not detect any MCP tools in src/mcp/mod.rs — tool-marking convention changed?"
);
let mem = read("MEMORY.md");
let needle = "### MCP tool count: ";
if let Some(idx) = mem.find(needle) {
let after = &mem[idx + needle.len()..];
let claimed: usize = after
.chars()
.take_while(|c| c.is_ascii_digit())
.collect::<String>()
.parse()
.unwrap_or(0);
assert_eq!(
claimed, actual,
"MEMORY.md MCP tool count stale: code = {actual}, MEMORY says = {claimed}"
);
}
}
fn cli_command_variants() -> Vec<String> {
let parser = read("src/cli/parser.rs");
let start = parser
.find("pub enum Commands")
.unwrap_or_else(|| panic!("src/cli/parser.rs missing `pub enum Commands`"));
let rest = &parser[start..];
let mut variants = Vec::new();
let mut depth: i32 = 0;
let mut entered = false;
for raw in rest.lines() {
let line = raw;
let trimmed = line.trim_start();
if entered
&& depth == 1
&& !trimmed.is_empty()
&& !trimmed.starts_with("//")
&& !trimmed.starts_with('#')
&& let Some(t) = trimmed.strip_suffix('{')
{
let ident = t.split_whitespace().next().unwrap_or("");
if !ident.is_empty() {
let first = ident.chars().next().unwrap();
if first.is_ascii_uppercase() {
variants.push(ident.to_owned());
}
}
}
for ch in line.chars() {
match ch {
'{' => depth += 1,
'}' => {
depth -= 1;
if entered && depth == 0 {
return variants;
}
}
_ => {}
}
}
if !entered && depth == 1 {
entered = true;
}
}
variants
}
#[test]
fn cli_command_set_matches_module_summary() {
let variants = cli_command_variants();
assert!(
variants.len() >= 2,
"could not detect any CLI command variants in src/cli/parser.rs — parser shape changed? got {variants:?}"
);
let arch = read("ARCHITECTURE.md").to_lowercase();
let readme = read("README.md").to_lowercase();
let missing: Vec<&String> = variants
.iter()
.filter(|v| {
let needle = v.to_lowercase();
!arch.contains(&needle) && !readme.contains(&needle)
})
.collect();
assert!(
missing.is_empty(),
"CLI variants missing from ARCHITECTURE.md / README.md: {missing:?} (rename or add a doc mention)"
);
}