mdbook_lint_core/
lib.rs

1//! Core linting engine for mdbook-lint
2//!
3//! This crate provides the foundational infrastructure for markdown linting with mdBook support.
4//! It defines the core abstractions and engine that powers mdbook-lint's rule-based linting system.
5//!
6//! # Overview
7//!
8//! The `mdbook-lint-core` crate provides:
9//! - **Plugin-based architecture** for extensible rule sets
10//! - **AST and text-based linting** with efficient document processing
11//! - **Violation reporting** with detailed position tracking and severity levels
12//! - **Automatic fix infrastructure** for correctable violations
13//! - **Configuration system** for customizing rule behavior
14//! - **Document abstraction** with markdown parsing via comrak
15//!
16//! # Architecture
17//!
18//! The core follows a plugin-based architecture where rules are provided by external crates:
19//!
20//! ```text
21//! ┌─────────────────┐
22//! │   Application   │
23//! └────────┬────────┘
24//!          │
25//! ┌────────▼────────┐
26//! │  PluginRegistry │ ◄─── Registers rule providers
27//! └────────┬────────┘
28//!          │
29//! ┌────────▼────────┐
30//! │   LintEngine    │ ◄─── Orchestrates linting
31//! └────────┬────────┘
32//!          │
33//! ┌────────▼────────┐
34//! │     Rules       │ ◄─── Individual rule implementations
35//! └─────────────────┘
36//! ```
37//!
38//! # Basic Usage
39//!
40//! ## Creating a Lint Engine
41//!
42//! ```rust
43//! use mdbook_lint_core::{PluginRegistry, Document};
44//! use std::path::PathBuf;
45//!
46//! // Create an empty engine (no rules registered)
47//! let registry = PluginRegistry::new();
48//! let engine = registry.create_engine()?;
49//!
50//! // Lint a document
51//! let document = Document::new("# Hello\n\nWorld".to_string(), PathBuf::from("test.md"))?;
52//! let violations = engine.lint_document(&document)?;
53//!
54//! // No violations since no rules are registered
55//! assert_eq!(violations.len(), 0);
56//! # Ok::<(), Box<dyn std::error::Error>>(())
57//! ```
58//!
59//! ## With Rule Providers
60//!
61//! ```rust,no_run
62//! use mdbook_lint_core::{PluginRegistry, Document};
63//! // Assumes mdbook-lint-rulesets is available
64//! // use mdbook_lint_rulesets::{StandardRuleProvider, MdBookRuleProvider};
65//! use std::path::PathBuf;
66//!
67//! let mut registry = PluginRegistry::new();
68//!
69//! // Register rule providers
70//! // registry.register_provider(Box::new(StandardRuleProvider))?;
71//! // registry.register_provider(Box::new(MdBookRuleProvider))?;
72//!
73//! // Create engine with registered rules
74//! let engine = registry.create_engine()?;
75//!
76//! // Lint a document
77//! let content = "# Title\n\n\n\nToo many blank lines";
78//! let document = Document::new(content.to_string(), PathBuf::from("test.md"))?;
79//! let violations = engine.lint_document(&document)?;
80//!
81//! // Process violations
82//! for violation in violations {
83//!     println!("{}:{} - {}", violation.rule_id, violation.line, violation.message);
84//! }
85//! # Ok::<(), Box<dyn std::error::Error>>(())
86//! ```
87//!
88//! # Key Types
89//!
90//! ## Document
91//!
92//! Represents a markdown file with its content and metadata:
93//!
94//! ```rust
95//! use mdbook_lint_core::Document;
96//! use std::path::PathBuf;
97//! use comrak::Arena;
98//!
99//! let doc = Document::new(
100//!     "# My Document\n\nContent here".to_string(),
101//!     PathBuf::from("doc.md")
102//! )?;
103//!
104//! // Parse AST with comrak Arena
105//! let arena = Arena::new();
106//! let ast = doc.parse_ast(&arena);
107//!
108//! // Get document lines
109//! let lines = &doc.lines;
110//! # Ok::<(), Box<dyn std::error::Error>>(())
111//! ```
112//!
113//! ## Violation
114//!
115//! Represents a linting violation with location and optional fix:
116//!
117//! ```rust
118//! use mdbook_lint_core::violation::{Violation, Severity, Fix, Position};
119//!
120//! let violation = Violation {
121//!     rule_id: "MD001".to_string(),
122//!     rule_name: "heading-increment".to_string(),
123//!     message: "Heading levels should increment by one".to_string(),
124//!     line: 5,
125//!     column: 1,
126//!     severity: Severity::Warning,
127//!     fix: Some(Fix {
128//!         description: "Change heading level".to_string(),
129//!         replacement: Some("## Correct Level".to_string()),
130//!         start: Position { line: 5, column: 1 },
131//!         end: Position { line: 5, column: 20 },
132//!     }),
133//! };
134//! ```
135//!
136//! ## Rule Traits
137//!
138//! Rules can be implemented using different traits based on their needs:
139//!
140//! - `Rule` - Base trait for all rules
141//! - `AstRule` - For rules that analyze the markdown AST
142//! - `TextRule` - For rules that analyze raw text
143//! - `RuleWithConfig` - For rules that support configuration
144//!
145//! # Configuration
146//!
147//! Rules can be configured through TOML configuration files:
148//!
149//! ```toml
150//! # .mdbook-lint.toml
151//! [rules.MD013]
152//! line_length = 120
153//! code_blocks = false
154//!
155//! [rules.MD009]
156//! br_spaces = 2
157//!
158//! # Disable specific rules
159//! [rules]
160//! MD002 = false
161//! MD041 = false
162//! ```
163//!
164//! # Features
165//!
166//! This crate has no optional features. All functionality is included by default.
167
168pub mod config;
169pub mod deduplication;
170pub mod document;
171pub mod engine;
172pub mod error;
173pub mod registry;
174pub mod rule;
175pub mod test_helpers;
176pub mod violation;
177
178// Re-export core types for convenience
179pub use config::Config;
180pub use document::Document;
181pub use engine::{LintEngine, PluginRegistry, RuleProvider};
182pub use error::{
183    ConfigError, DocumentError, ErrorContext, IntoMdBookLintError, MdBookLintError, MdlntError,
184    PluginError, Result, RuleError,
185};
186pub use registry::RuleRegistry;
187pub use rule::{AstRule, Rule, RuleCategory, RuleMetadata, RuleStability};
188pub use violation::{Severity, Violation};
189
190/// Current version of mdbook-lint-core
191pub const VERSION: &str = env!("CARGO_PKG_VERSION");
192
193/// Human-readable name
194pub const NAME: &str = "mdbook-lint-core";
195
196/// Description
197pub const DESCRIPTION: &str = "Core linting engine for mdbook-lint";
198
199/// Create a lint engine with all available rules (standard + mdBook)
200/// Note: Requires mdbook-lint-rulesets dependency for rule providers
201pub fn create_engine_with_all_rules() -> LintEngine {
202    panic!(
203        "create_engine_with_all_rules() is deprecated. Use mdbook-lint-rulesets crate providers directly."
204    )
205}
206
207/// Create a lint engine with only standard markdown rules
208/// Note: Requires mdbook-lint-rulesets dependency for rule providers
209pub fn create_standard_engine() -> LintEngine {
210    panic!(
211        "create_standard_engine() is deprecated. Use mdbook-lint-rulesets crate providers directly."
212    )
213}
214
215/// Create a lint engine with only mdBook-specific rules
216/// Note: Requires mdbook-lint-rulesets dependency for rule providers
217pub fn create_mdbook_engine() -> LintEngine {
218    panic!(
219        "create_mdbook_engine() is deprecated. Use mdbook-lint-rulesets crate providers directly."
220    )
221}
222
223/// Common imports
224pub mod prelude {
225    pub use crate::{
226        Document,
227        engine::{LintEngine, PluginRegistry, RuleProvider},
228        error::{ErrorContext, IntoMdBookLintError, MdBookLintError, MdlntError, Result},
229        registry::RuleRegistry,
230        rule::{AstRule, Rule, RuleCategory, RuleMetadata, RuleStability},
231        violation::{Severity, Violation},
232    };
233}
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238
239    #[test]
240    fn test_version_info() {
241        assert_eq!(NAME, "mdbook-lint-core");
242        assert!(DESCRIPTION.contains("linting engine"));
243    }
244
245    #[test]
246    #[should_panic]
247    fn test_create_all_rules_engine_deprecated() {
248        create_engine_with_all_rules();
249    }
250
251    #[test]
252    #[should_panic]
253    fn test_create_standard_engine_deprecated() {
254        create_standard_engine();
255    }
256
257    #[test]
258    #[should_panic]
259    fn test_create_mdbook_engine_deprecated() {
260        create_mdbook_engine();
261    }
262
263    #[test]
264    fn test_basic_engine_creation() {
265        let engine = LintEngine::new();
266        assert_eq!(engine.available_rules().len(), 0);
267    }
268
269    #[test]
270    fn test_plugin_registry_creation() {
271        let registry = PluginRegistry::new();
272        assert_eq!(registry.providers().len(), 0);
273    }
274
275    #[test]
276    fn test_rule_registry_creation() {
277        let registry = RuleRegistry::new();
278        assert!(registry.is_empty());
279    }
280}