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}