mdbook_lint_core/rules/
mdbook025.rs1use crate::error::Result;
7use crate::rule::{AstRule, RuleCategory, RuleMetadata};
8use crate::{Document, violation::Violation};
9use comrak::nodes::AstNode;
10
11pub struct MDBOOK025;
17
18impl MDBOOK025 {
19 pub fn new() -> Self {
21 Self
22 }
23}
24
25impl Default for MDBOOK025 {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31impl AstRule for MDBOOK025 {
32 fn id(&self) -> &'static str {
33 "MDBOOK025"
34 }
35
36 fn name(&self) -> &'static str {
37 "summary-multiple-h1-allowed"
38 }
39
40 fn description(&self) -> &'static str {
41 "Multiple H1 headings are allowed in SUMMARY.md files"
42 }
43
44 fn metadata(&self) -> RuleMetadata {
45 RuleMetadata::stable(RuleCategory::Structure)
46 .introduced_in("mdbook-lint v0.4.0")
47 .overrides("MD025")
48 }
49
50 fn check_ast<'a>(&self, document: &Document, _ast: &'a AstNode<'a>) -> Result<Vec<Violation>> {
51 if let Some(filename) = document.path.file_name()
53 && filename == "SUMMARY.md"
54 {
55 return Ok(Vec::new());
57 }
58
59 Ok(Vec::new())
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::rule::Rule;
69 use std::path::PathBuf;
70
71 #[test]
72 fn test_mdbook025_summary_file_multiple_h1s_allowed() {
73 let content = r#"# Summary
74
75[Introduction](introduction.md)
76
77# Part I: Getting Started
78
79- [Chapter 1](chapter1.md)
80
81# Part II: Advanced Topics
82
83- [Chapter 2](chapter2.md)
84"#;
85 let document = Document::new(content.to_string(), PathBuf::from("SUMMARY.md")).unwrap();
86 let rule = MDBOOK025::new();
87 let violations = rule.check(&document).unwrap();
88
89 assert_eq!(violations.len(), 0);
91 }
92
93 #[test]
94 fn test_mdbook025_non_summary_file_ignored() {
95 let content = r#"# First H1 heading
96Some content here.
97
98# Second H1 heading
99More content.
100"#;
101 let document = Document::new(content.to_string(), PathBuf::from("chapter.md")).unwrap();
102 let rule = MDBOOK025::new();
103 let violations = rule.check(&document).unwrap();
104
105 assert_eq!(violations.len(), 0);
107 }
108
109 #[test]
110 fn test_mdbook025_summary_with_single_h1() {
111 let content = r#"# Summary
112
113- [Chapter 1](chapter1.md)
114- [Chapter 2](chapter2.md)
115"#;
116 let document = Document::new(content.to_string(), PathBuf::from("SUMMARY.md")).unwrap();
117 let rule = MDBOOK025::new();
118 let violations = rule.check(&document).unwrap();
119
120 assert_eq!(violations.len(), 0);
122 }
123
124 #[test]
125 fn test_mdbook025_rule_metadata() {
126 use crate::rule::AstRule;
127 let rule = MDBOOK025::new();
128
129 assert_eq!(AstRule::id(&rule), "MDBOOK025");
130 assert_eq!(AstRule::name(&rule), "summary-multiple-h1-allowed");
131 assert!(AstRule::description(&rule).contains("SUMMARY.md"));
132
133 let metadata = AstRule::metadata(&rule);
134 assert_eq!(metadata.category, RuleCategory::Structure);
135 assert!(metadata.overrides.as_ref().unwrap().contains("MD025"));
136 }
137}