mdbook_check_missing_md/
lib.rs1pub mod md_files;
2pub mod summary;
3
4use std::collections::HashSet;
5
6use mdbook::renderer::RenderContext;
7use anyhow::Error;
8
9use crate::md_files::collect_md_files;
10use crate::summary::get_summary_md_files;
11
12pub fn run(ctx: &RenderContext) -> Result<(), Error> {
13 let root = ctx.root.join("src");
14 let summary_path = root.join("SUMMARY.md");
15
16 let mut all_md_files = HashSet::new();
17 collect_md_files(&root, &mut all_md_files)?;
18
19 all_md_files.remove(&summary_path.canonicalize()?);
21
22 let summary_md_files = get_summary_md_files(&summary_path)?;
23
24 let missing_in_summary: HashSet<_> = all_md_files.difference(&summary_md_files).collect();
25
26 if !missing_in_summary.is_empty() {
27 eprintln!("Error: The following .md files are not listed in SUMMARY.md:");
28 for file in missing_in_summary {
29 eprintln!("{}", file.display());
30 }
31 std::process::exit(1);
32 }
33
34 println!("All files are listed in SUMMARY.md");
35 Ok(())
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41 use std::fs::{self, File};
42 use std::io::Write;
43
44 use tempfile::tempdir;
45 use mdbook::renderer::RenderContext;
46 use mdbook::book::Book;
47 use mdbook::config::Config;
48
49 #[test]
50 fn test_check_missing() {
51 let dir = tempdir().unwrap();
52 let src_dir = dir.path().join("src");
53 let summary_path = src_dir.join("SUMMARY.md");
54
55 fs::create_dir_all(&src_dir).unwrap();
57 let md_files = vec!["intro.md", "chapter_1.md", "chapter_1/section_1.md"];
58 for file in &md_files {
59 let file_path = src_dir.join(file);
60 fs::create_dir_all(file_path.parent().unwrap()).unwrap();
61 File::create(&file_path).unwrap();
62 }
63
64 let summary_content = r#"
66 * [Introduction](intro.md)
67 * [Chapter 1](chapter_1.md)
68 * [Section 1](chapter_1/section_1.md)
69 "#;
70 let mut summary_file = File::create(&summary_path).unwrap();
71 summary_file.write_all(summary_content.as_bytes()).unwrap();
72
73 let ctx = RenderContext::new(
74 dir.path().to_path_buf(),
75 Book::new(),
76 Config::default(),
77 dir.path().to_path_buf()
78 );
79
80 let result = run(&ctx);
81
82 assert!(result.is_ok());
83 }
84}