Skip to main content

mdwright_lint/
rule.rs

1//! The `LintRule` trait — `mdwright`'s open extension point.
2//!
3//! A rule is any `Send + Sync` value that names itself, describes
4//! itself, and inspects a [`Document`] to produce [`Diagnostic`]s.
5//! The standard library in [`crate::stdlib`] ships fifteen
6//! implementors; user code adds more by implementing this trait on
7//! a struct and dropping it into a [`RuleSet`](crate::RuleSet).
8//!
9//! ## Identity
10//!
11//! Each rule carries a stable kebab-case name. The name is the
12//! identifier used in CLI flags (`--rules orphan-reference-link`),
13//! configuration files, suppression comments, and diagnostic output.
14//! Names must be unique within any `RuleSet` — duplicate insertion
15//! fails (see [`RuleSet::add`](crate::RuleSet::add)).
16//!
17//! ## Emit pattern
18//!
19//! Rule implementations append [`Diagnostic`]s to the supplied
20//! `Vec`. They should leave the diagnostic's `rule` field empty —
21//! the dispatcher stamps it from `self.name()` after the call
22//! returns, so rule code does not repeat its own name on every emit.
23
24use crate::diagnostic::Diagnostic;
25use mdwright_document::Document;
26
27/// A lint check. Implementors may be unit structs (stdlib rules) or
28/// carry configuration (regex patterns, allowlists, …).
29pub trait LintRule: Send + Sync {
30    /// Stable kebab-case identifier. Must be unique within any
31    /// [`RuleSet`](crate::RuleSet).
32    fn name(&self) -> &str;
33
34    /// One-line summary for `mdwright list-rules`.
35    fn description(&self) -> &str;
36
37    /// Whether this rule is enabled in
38    /// [`RuleSet::stdlib_defaults`](crate::RuleSet::stdlib_defaults).
39    /// Most rules are on by default; the few opinionated or
40    /// repair-focused checks return `false`.
41    fn is_default(&self) -> bool {
42        true
43    }
44
45    /// Advisory rules emit informational diagnostics that do not
46    /// fail `mdwright check --check`. Their output still prints.
47    fn is_advisory(&self) -> bool {
48        false
49    }
50
51    /// Whether this rule can emit a [`crate::Fix`]. Used by
52    /// `mdwright list-rules` and the generated rule docs. Defaults
53    /// to `false`; stdlib rules that emit fixes override.
54    fn produces_fix(&self) -> bool {
55        false
56    }
57
58    /// Long-form explanation used by `mdwright explain <rule>` and
59    /// the per-rule pages under `docs/rules/`. Returns an empty
60    /// string by default so existing third-party rules continue to
61    /// compile; stdlib rules override with a multi-paragraph
62    /// markdown body (no frontmatter).
63    fn explain(&self) -> &str {
64        ""
65    }
66
67    /// Run the check against a parsed document. Append diagnostics
68    /// to `out`. The dispatcher fills in each diagnostic's `rule`
69    /// and `advisory` fields from `self.name()` and
70    /// `self.is_advisory()` after the call returns.
71    fn check(&self, doc: &Document, out: &mut Vec<Diagnostic>);
72}