1use std::path::Path;
14
15pub mod broker;
16pub mod emit;
17pub mod scenarios;
18pub mod schema_md;
19pub mod snippets;
20
21pub fn sync_snippets(content_dir: &Path, crates_dir: &Path) -> anyhow::Result<usize> {
29 use std::fs;
30 let mut changed = 0;
31 let mut stack = vec![content_dir.to_path_buf()];
32 while let Some(d) = stack.pop() {
33 for entry in fs::read_dir(&d)? {
34 let path = entry?.path();
35 if path.is_dir() {
36 stack.push(path);
37 } else if path.extension().is_some_and(|e| e == "md") {
38 let before = fs::read_to_string(&path)?;
39 let after = snippets::sync_markdown(&before, crates_dir)
40 .map_err(|e| anyhow::anyhow!("{}: {e}", path.display()))?;
41 if after != before {
42 fs::write(&path, after)?;
43 changed += 1;
44 }
45 }
46 }
47 }
48 Ok(changed)
49}
50
51#[cfg(test)]
52mod tests {
53 use std::fs;
54
55 #[test]
56 fn sync_snippets_rewrites_nested_markdown_and_is_idempotent() {
57 let dir = tempfile::tempdir().unwrap();
58 let crates = dir.path().join("crates");
59 let content = dir.path().join("content");
60 fs::create_dir_all(crates.join("c/examples")).unwrap();
61 fs::create_dir_all(content.join("sub")).unwrap();
62 fs::write(
63 crates.join("c/examples/e.rs"),
64 "// docs:begin a\nlet z = 9;\n// docs:end a\n",
65 )
66 .unwrap();
67 let md = content.join("sub/page.md");
68 fs::write(
69 &md,
70 "intro\n<!-- snippet: c/examples/e.rs#a -->\nOLD\n<!-- /snippet -->\nend\n",
71 )
72 .unwrap();
73 fs::write(content.join("ignore.txt"), "OLD").unwrap();
75
76 let changed = super::sync_snippets(&content, &crates).unwrap();
77 assert2::assert!(changed == 1);
78 let out = fs::read_to_string(&md).unwrap();
79 assert2::assert!(out.contains("```rust\nlet z = 9;\n```"));
80 assert2::assert!(!out.contains("OLD"));
81 assert2::assert!(fs::read_to_string(content.join("ignore.txt")).unwrap() == "OLD");
82
83 assert2::assert!(super::sync_snippets(&content, &crates).unwrap() == 0);
85 }
86}