Skip to main content

normalize_native_rules/
lib.rs

1//! Native rule checks for normalize.
2//!
3//! Implements stale-summary, check-refs, stale-docs, check-examples, ratchet, budget,
4//! long-file, high-complexity, long-function, high-fan-out, and high-fan-in as pure
5//! Rust checks. These are the "native engine" checks invoked by
6//! `normalize rules run --engine native`.
7
8pub mod boundary_violations;
9pub mod budget;
10pub mod cache;
11pub mod check_examples;
12pub mod check_refs;
13pub mod dead_parameter;
14pub mod high_complexity;
15pub mod high_fan_in;
16pub mod high_fan_out;
17pub mod long_file;
18pub mod long_function;
19pub mod ratchet;
20pub mod stale_doc;
21pub mod stale_docs;
22pub mod stale_summary;
23pub mod walk;
24
25pub use cache::{
26    FileRule, FindingsCache, file_mtime_nanos as cache_file_mtime_nanos, run_file_rule,
27};
28
29pub use boundary_violations::{
30    Boundary, BoundaryViolationsConfig, build_boundary_violations_report, parse_boundary,
31};
32pub use budget::{BudgetRulesReport, build_budget_report};
33pub use check_examples::build_check_examples_report;
34pub use check_refs::build_check_refs_report;
35pub use dead_parameter::build_dead_parameter_report;
36pub use high_complexity::build_high_complexity_report;
37pub use high_fan_in::build_high_fan_in_report;
38pub use high_fan_out::build_high_fan_out_report;
39pub use long_file::build_long_file_report;
40pub use long_function::build_long_function_report;
41pub use ratchet::{RatchetRulesReport, build_ratchet_report};
42pub use stale_doc::{StaleDocConfig, build_stale_doc_report};
43pub use stale_docs::build_stale_docs_report;
44pub use stale_summary::{build_missing_summary_report, build_stale_summary_report};
45
46/// Static descriptor for a native rule's default metadata.
47///
48/// `default_severity` is the baked-in severity from the rule author. At runtime
49/// the actual severity may differ: `normalize rules run` applies any
50/// `[rules.rule."rule-id"]` overrides from the project's `normalize.toml` via
51/// `normalize_rules::apply_native_rules_config` before presenting findings to the user.
52pub struct NativeRuleDescriptor {
53    /// Unique rule identifier (e.g. `"stale-summary"`).
54    pub id: &'static str,
55    /// Default severity before any project-level override (`"error"`, `"warning"`, or `"info"`).
56    pub default_severity: &'static str,
57    /// Short human-readable description of what the rule checks.
58    pub message: &'static str,
59    /// Tags used for grouping and filtering (e.g. `&["docs", "quality"]`).
60    pub tags: &'static [&'static str],
61    /// Whether the rule is enabled by default (before any project-level override).
62    /// Advisory rules like `long-file` default to `false`.
63    pub default_enabled: bool,
64}
65
66/// All native rules with their default metadata.
67pub const NATIVE_RULES: &[NativeRuleDescriptor] = &[
68    NativeRuleDescriptor {
69        id: "boundary-violations",
70        default_severity: "warning",
71        message: "Import crosses a configured directory boundary (e.g. services/ → cli/)",
72        tags: &["architecture", "boundaries"],
73        default_enabled: false,
74    },
75    NativeRuleDescriptor {
76        id: "broken-ref",
77        default_severity: "warning",
78        message: "Backtick reference in docs/comments doesn't resolve to a known symbol or file",
79        tags: &["correctness", "documentation"],
80        default_enabled: true,
81    },
82    NativeRuleDescriptor {
83        id: "missing-summary",
84        default_severity: "error",
85        message: "Directory is missing a required doc file (default: SUMMARY.md; configurable via filenames and paths)",
86        tags: &["documentation"],
87        default_enabled: true,
88    },
89    NativeRuleDescriptor {
90        id: "stale-summary",
91        default_severity: "error",
92        message: "Doc file hasn't been updated since files in the directory changed (default: SUMMARY.md; configurable via filenames and paths)",
93        tags: &["documentation"],
94        default_enabled: true,
95    },
96    NativeRuleDescriptor {
97        id: "stale-doc",
98        default_severity: "warning",
99        message: "Documentation file may be stale — a strongly co-changed code file was updated more recently",
100        tags: &["docs", "freshness"],
101        default_enabled: false,
102    },
103    NativeRuleDescriptor {
104        id: "missing-example",
105        default_severity: "warning",
106        message: "Example referenced in docs doesn't appear in the source file",
107        tags: &["documentation"],
108        default_enabled: true,
109    },
110    NativeRuleDescriptor {
111        id: "ratchet/complexity",
112        default_severity: "error",
113        message: "Cyclomatic complexity has regressed past the ratchet baseline",
114        tags: &["quality", "complexity"],
115        default_enabled: true,
116    },
117    NativeRuleDescriptor {
118        id: "ratchet/call-complexity",
119        default_severity: "error",
120        message: "Transitive call complexity has regressed past the ratchet baseline",
121        tags: &["quality", "complexity"],
122        default_enabled: true,
123    },
124    NativeRuleDescriptor {
125        id: "ratchet/line-count",
126        default_severity: "error",
127        message: "File line count has regressed past the ratchet baseline",
128        tags: &["quality"],
129        default_enabled: true,
130    },
131    NativeRuleDescriptor {
132        id: "ratchet/function-count",
133        default_severity: "error",
134        message: "Function count has regressed past the ratchet baseline",
135        tags: &["quality"],
136        default_enabled: true,
137    },
138    NativeRuleDescriptor {
139        id: "ratchet/class-count",
140        default_severity: "error",
141        message: "Class count has regressed past the ratchet baseline",
142        tags: &["quality"],
143        default_enabled: true,
144    },
145    NativeRuleDescriptor {
146        id: "ratchet/comment-line-count",
147        default_severity: "error",
148        message: "Comment line count has regressed past the ratchet baseline",
149        tags: &["quality"],
150        default_enabled: true,
151    },
152    NativeRuleDescriptor {
153        id: "budget/lines",
154        default_severity: "error",
155        message: "Line diff exceeds configured budget limit",
156        tags: &["quality", "budget"],
157        default_enabled: true,
158    },
159    NativeRuleDescriptor {
160        id: "budget/functions",
161        default_severity: "error",
162        message: "Function diff exceeds configured budget limit",
163        tags: &["quality", "budget"],
164        default_enabled: true,
165    },
166    NativeRuleDescriptor {
167        id: "budget/classes",
168        default_severity: "error",
169        message: "Class diff exceeds configured budget limit",
170        tags: &["quality", "budget"],
171        default_enabled: true,
172    },
173    NativeRuleDescriptor {
174        id: "budget/modules",
175        default_severity: "error",
176        message: "Module diff exceeds configured budget limit",
177        tags: &["quality", "budget"],
178        default_enabled: true,
179    },
180    NativeRuleDescriptor {
181        id: "budget/todos",
182        default_severity: "error",
183        message: "TODO/FIXME diff exceeds configured budget limit",
184        tags: &["quality", "budget"],
185        default_enabled: true,
186    },
187    NativeRuleDescriptor {
188        id: "budget/complexity-delta",
189        default_severity: "error",
190        message: "Complexity delta exceeds configured budget limit",
191        tags: &["quality", "budget", "complexity"],
192        default_enabled: true,
193    },
194    NativeRuleDescriptor {
195        id: "budget/dependencies",
196        default_severity: "error",
197        message: "Dependency diff exceeds configured budget limit",
198        tags: &["quality", "budget"],
199        default_enabled: true,
200    },
201    NativeRuleDescriptor {
202        id: "high-fan-out",
203        default_severity: "warning",
204        message: "File imports from too many other modules (default threshold: 20) — high coupling smell",
205        tags: &["architecture", "coupling"],
206        default_enabled: false,
207    },
208    NativeRuleDescriptor {
209        id: "high-fan-in",
210        default_severity: "warning",
211        message: "File is imported by too many other modules (default threshold: 20) — fragile shared dependency",
212        tags: &["architecture", "coupling"],
213        default_enabled: false,
214    },
215    NativeRuleDescriptor {
216        id: "long-file",
217        default_severity: "warning",
218        message: "Source file exceeds line count threshold (default: 500 lines)",
219        tags: &["quality"],
220        default_enabled: false,
221    },
222    NativeRuleDescriptor {
223        id: "high-complexity",
224        default_severity: "warning",
225        message: "Function exceeds cyclomatic complexity threshold (default: 20)",
226        tags: &["quality", "complexity"],
227        default_enabled: false,
228    },
229    NativeRuleDescriptor {
230        id: "long-function",
231        default_severity: "warning",
232        message: "Function exceeds line count threshold (default: 100 lines)",
233        tags: &["quality"],
234        default_enabled: false,
235    },
236    NativeRuleDescriptor {
237        id: "dead-parameter",
238        default_severity: "warning",
239        message: "Function parameter is never used in the function body",
240        tags: &["correctness", "unused"],
241        default_enabled: false,
242    },
243];