mdbook_lint_core/
rules.rs

1//! Consolidated rules module for mdbook-lint
2//!
3//! This module organizes all linting rules available in mdbook-lint:
4//! - **Standard Rules (MD001-MD059)**: 59 markdownlint-compatible rules
5//! - **mdBook Rules (MDBOOK001-004)**: 4 mdBook-specific rules
6//!
7//! Total: 63 comprehensive rules for mdBook documentation projects.
8
9// Standard markdownlint rules (MD001-MD059)
10pub mod standard;
11
12// mdBook-specific rules (MDBOOK001-007, MDBOOK025)
13pub mod mdbook001;
14pub mod mdbook002;
15pub mod mdbook003;
16pub mod mdbook004;
17pub mod mdbook005;
18pub mod mdbook006;
19pub mod mdbook007;
20pub mod mdbook025;
21
22use crate::{engine::RuleProvider, registry::RuleRegistry};
23
24/// Provider for mdBook-specific linting rules
25///
26/// This provider includes mdBook-specific rules (MDBOOK001-007) that check
27/// for mdBook conventions and best practices:
28/// - Code block language tags for proper syntax highlighting
29/// - Internal link validation within the book structure
30/// - SUMMARY.md format validation
31/// - Unique chapter title enforcement
32/// - Orphaned file detection
33/// - Cross-reference anchor validation
34/// - Include directive validation
35///
36/// # Rule Coverage
37///
38/// - **MDBOOK001**: code-block-language - Code blocks should have language tags
39/// - **MDBOOK002**: internal-link-validation - Internal links must resolve
40/// - **MDBOOK003**: summary-structure - SUMMARY.md format validation
41/// - **MDBOOK004**: no-duplicate-chapter-titles - Unique chapter titles
42/// - **MDBOOK005**: orphaned-files - Detect files not referenced in SUMMARY.md
43/// - **MDBOOK006**: internal-cross-references - Validate anchor links between chapters
44/// - **MDBOOK007**: include-validation - Validate include directive paths and syntax
45pub struct MdBookRuleProvider;
46
47impl RuleProvider for MdBookRuleProvider {
48    fn provider_id(&self) -> &'static str {
49        "mdbook"
50    }
51
52    fn description(&self) -> &'static str {
53        "mdBook-specific linting rules for preprocessor functionality"
54    }
55
56    fn version(&self) -> &'static str {
57        env!("CARGO_PKG_VERSION")
58    }
59
60    fn register_rules(&self, registry: &mut RuleRegistry) {
61        // Register mdBook-specific rules
62        registry.register(Box::new(mdbook001::MDBOOK001));
63        registry.register(Box::new(mdbook002::MDBOOK002));
64        registry.register(Box::new(mdbook003::MDBOOK003));
65        registry.register(Box::new(mdbook004::MDBOOK004));
66        registry.register(Box::new(mdbook005::MDBOOK005::default()));
67        registry.register(Box::new(mdbook006::MDBOOK006::default()));
68        registry.register(Box::new(mdbook007::MDBOOK007::default()));
69        registry.register(Box::new(mdbook025::MDBOOK025));
70    }
71
72    fn rule_ids(&self) -> Vec<&'static str> {
73        vec![
74            "MDBOOK001",
75            "MDBOOK002",
76            "MDBOOK003",
77            "MDBOOK004",
78            "MDBOOK005",
79            "MDBOOK006",
80            "MDBOOK007",
81            "MDBOOK025",
82        ]
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_mdbook_provider_metadata() {
92        let provider = MdBookRuleProvider;
93        assert_eq!(provider.provider_id(), "mdbook");
94        assert!(provider.description().contains("mdBook"));
95        assert!(!provider.version().is_empty());
96    }
97
98    #[test]
99    fn test_mdbook_provider_rule_count() {
100        let provider = MdBookRuleProvider;
101        let rule_ids = provider.rule_ids();
102
103        // Should have 8 mdBook rules (MDBOOK001-007, MDBOOK025)
104        assert_eq!(rule_ids.len(), 8);
105
106        // Check all mdBook rules are present
107        assert!(rule_ids.contains(&"MDBOOK001"));
108        assert!(rule_ids.contains(&"MDBOOK002"));
109        assert!(rule_ids.contains(&"MDBOOK003"));
110        assert!(rule_ids.contains(&"MDBOOK004"));
111        assert!(rule_ids.contains(&"MDBOOK005"));
112        assert!(rule_ids.contains(&"MDBOOK006"));
113        assert!(rule_ids.contains(&"MDBOOK007"));
114        assert!(rule_ids.contains(&"MDBOOK025"));
115
116        // Should not contain standard rules
117        assert!(!rule_ids.contains(&"MD001"));
118    }
119
120    #[test]
121    fn test_mdbook_provider_registration() {
122        let mut registry = RuleRegistry::new();
123        let provider = MdBookRuleProvider;
124
125        // Registry should be empty initially
126        assert_eq!(registry.len(), 0);
127
128        // Register the provider's rules
129        provider.register_rules(&mut registry);
130
131        // Should now have all mdBook rules
132        assert_eq!(registry.len(), 8);
133
134        // Check specific rules are registered
135        assert!(registry.get_rule("MDBOOK001").is_some());
136        assert!(registry.get_rule("MDBOOK002").is_some());
137        assert!(registry.get_rule("MDBOOK003").is_some());
138        assert!(registry.get_rule("MDBOOK004").is_some());
139        assert!(registry.get_rule("MDBOOK005").is_some());
140        assert!(registry.get_rule("MDBOOK006").is_some());
141        assert!(registry.get_rule("MDBOOK007").is_some());
142        assert!(registry.get_rule("MDBOOK025").is_some());
143        assert!(registry.get_rule("MD001").is_none());
144    }
145}