Skip to main content

pedant_core/check_config/
file.rs

1use std::collections::BTreeMap;
2use std::sync::{Arc, LazyLock};
3
4use serde::Deserialize;
5
6use super::gate::GateConfig;
7use super::naming::{NamingCheck, NamingOverride};
8use super::pattern::{PatternCheck, PatternOverride};
9use super::string_list::deserialize_arc_str_slice;
10
11/// One `item-visibility-policy` rule: a named item at a path must have an
12/// exact visibility.
13#[derive(Debug, Deserialize, Clone)]
14#[serde(deny_unknown_fields)]
15pub struct ItemVisibilityRule {
16    /// Repository-relative source path the item must live in.
17    pub path: Box<str>,
18    /// Item kind: `struct`, `enum`, `union`, `trait`, or `fn`.
19    pub kind: Box<str>,
20    /// Exact item name.
21    pub name: Box<str>,
22    /// Required visibility: `private`, `pub`, `pub(crate)`, `pub(super)`,
23    /// or `pub(in <path>)`.
24    pub visibility: Box<str>,
25}
26
27/// One `feature-boundary` rule: a package feature must obey the named invariant.
28#[derive(Debug, Deserialize, Clone)]
29#[serde(deny_unknown_fields)]
30pub struct FeatureBoundaryRule {
31    /// The package whose feature is constrained.
32    pub package: Box<str>,
33    /// The feature name the rule applies to.
34    pub feature: Box<str>,
35    /// `no-default` (must not be reachable from any default feature) or
36    /// `dev-only` (may be enabled only through dev-dependency edges).
37    pub rule: Box<str>,
38}
39
40/// One `flat-module-family` rule: a prefixed module family under `parent` must
41/// live below `parent/package_root/`.
42#[derive(Debug, Deserialize, Clone)]
43#[serde(deny_unknown_fields)]
44pub struct FlatModuleFamily {
45    /// Directory (repo-relative) whose direct children are checked.
46    pub parent: Box<str>,
47    /// Sub-directory of `parent` where the family must live.
48    pub package_root: Box<str>,
49    /// Module-name prefix identifying family members.
50    pub prefix: Box<str>,
51}
52
53/// Deserialized `.pedant.toml` file with all check settings.
54#[derive(Debug, Deserialize, Default)]
55#[serde(deny_unknown_fields)]
56pub struct ConfigFile {
57    /// Security gate rules configuration.
58    #[serde(default)]
59    pub gate: GateConfig,
60    /// Depth limit for nesting checks (default: 3).
61    #[serde(default = "default_max_depth")]
62    pub max_depth: usize,
63    /// Branch count that triggers `else-chain` (default: 3).
64    #[serde(default = "default_else_chain_threshold")]
65    pub else_chain_threshold: usize,
66    /// Maximum parameter count before `high-param-count` fires (default: 5).
67    #[serde(default = "default_max_params")]
68    pub max_params: usize,
69    /// Body line count before `long-function-body` fires (default: 120).
70    #[serde(default = "default_max_function_body_lines")]
71    pub max_function_body_lines: usize,
72    /// File names treated as module roots by `module-root-definitions`
73    /// (default: `mod.rs`, `lib.rs`).
74    #[serde(
75        default = "default_module_root_files",
76        deserialize_with = "deserialize_arc_str_slice"
77    )]
78    pub module_root_files: Arc<[Arc<str>]>,
79    /// Line count at which `large-source-file` emits a `Warn` (default: 500).
80    #[serde(default = "default_source_file_warn_lines")]
81    pub source_file_warn_lines: usize,
82    /// Line count at which `large-source-file` emits a `Deny` (default: 1000).
83    #[serde(default = "default_source_file_deny_lines")]
84    pub source_file_deny_lines: usize,
85    /// Inherent-method count before `high-method-count` fires (default: 40).
86    #[serde(default = "default_max_methods")]
87    pub max_methods: usize,
88    /// Banned attribute patterns (e.g., `allow(dead_code)`).
89    #[serde(default)]
90    pub forbid_attributes: PatternCheck,
91    /// Banned type patterns (e.g., `Arc<String>`).
92    #[serde(default)]
93    pub forbid_types: PatternCheck,
94    /// Banned method call patterns (e.g., `.unwrap()`).
95    #[serde(default)]
96    pub forbid_calls: PatternCheck,
97    /// Banned macro patterns (e.g., `panic!`).
98    #[serde(default)]
99    pub forbid_macros: PatternCheck,
100    /// Thresholds for the generic-naming check.
101    #[serde(default)]
102    pub check_naming: NamingCheck,
103    /// Flag `if` inside `if`.
104    #[serde(default = "default_true")]
105    pub check_nested_if: bool,
106    /// Flag `if` inside `match` arm.
107    #[serde(default = "default_true")]
108    pub check_if_in_match: bool,
109    /// Flag `match` inside `match`.
110    #[serde(default = "default_true")]
111    pub check_nested_match: bool,
112    /// Flag `match` inside `if` branch.
113    #[serde(default = "default_true")]
114    pub check_match_in_if: bool,
115    /// Flag long `if/else if` chains.
116    #[serde(default = "default_true")]
117    pub check_else_chain: bool,
118    /// Flag any use of the `else` keyword.
119    #[serde(default)]
120    pub forbid_else: bool,
121    /// Flag any `unsafe` block.
122    #[serde(default = "default_true")]
123    pub forbid_unsafe: bool,
124    /// Flag dynamic dispatch in return types.
125    #[serde(default)]
126    pub check_dyn_return: bool,
127    /// Flag dynamic dispatch in function parameters.
128    #[serde(default)]
129    pub check_dyn_param: bool,
130    /// Flag `Vec<Box<dyn T>>` anywhere.
131    #[serde(default)]
132    pub check_vec_box_dyn: bool,
133    /// Flag dynamic dispatch in struct fields.
134    #[serde(default)]
135    pub check_dyn_field: bool,
136    /// Flag `.clone()` inside loop bodies.
137    #[serde(default)]
138    pub check_clone_in_loop: bool,
139    /// Flag `HashMap`/`HashSet` with default SipHash hasher.
140    #[serde(default)]
141    pub check_default_hasher: bool,
142    /// Flag disconnected type groups in a single file.
143    #[serde(default)]
144    pub check_mixed_concerns: bool,
145    /// Flag `#[cfg(test)] mod` blocks embedded in source files.
146    #[serde(default)]
147    pub check_inline_tests: bool,
148    /// Flag `let _ = expr` that discards a Result.
149    #[serde(default)]
150    pub check_let_underscore_result: bool,
151    /// Flag functions with too many parameters.
152    #[serde(default)]
153    pub check_high_param_count: bool,
154    /// Flag function bodies that exceed the line ceiling.
155    #[serde(default)]
156    pub check_long_function_body: bool,
157    /// Flag item definitions in module-root files.
158    #[serde(default)]
159    pub check_module_root_definitions: bool,
160    /// Flag source files that exceed the line ceiling.
161    #[serde(default)]
162    pub check_large_source_file: bool,
163    /// Flag god-object types by inherent-method count.
164    #[serde(default)]
165    pub check_high_method_count: bool,
166    /// Count pure forwarders toward `high-method-count`.
167    #[serde(default)]
168    pub count_forwarders: bool,
169    /// Enforce configured item-visibility policies.
170    #[serde(default = "default_true")]
171    pub check_item_visibility_policy: bool,
172    /// Item-visibility policy rules.
173    #[serde(default)]
174    pub item_visibility_policy: Vec<ItemVisibilityRule>,
175    /// Flag ungated test-only APIs under `src/`.
176    #[serde(default)]
177    pub check_ungated_test_api: bool,
178    /// Flag sibling `<stem>.rs` and `<stem>/` module roots.
179    #[serde(default)]
180    pub check_conflicting_module_root: bool,
181    /// Name globs that mark test-only APIs (default: `*_for_tests`).
182    #[serde(
183        default = "default_test_api_patterns",
184        deserialize_with = "deserialize_arc_str_slice"
185    )]
186    pub test_api_patterns: Arc<[Arc<str>]>,
187    /// Feature that must gate a test-only API (default: `test-support`).
188    #[serde(default = "default_test_support_feature")]
189    pub test_support_feature: Box<str>,
190    /// Enforce configured flat-module-family layout rules.
191    #[serde(default = "default_true")]
192    pub check_flat_module_family: bool,
193    /// Flat-module-family layout rules.
194    #[serde(default)]
195    pub flat_module_families: Vec<FlatModuleFamily>,
196    /// Enforce configured Cargo feature-boundary invariants.
197    #[serde(default = "default_true")]
198    pub check_feature_boundary: bool,
199    /// Cargo feature-boundary invariants.
200    #[serde(default)]
201    pub feature_boundaries: Vec<FeatureBoundaryRule>,
202    /// Flag types whose inherent impls span more than one file.
203    #[serde(default)]
204    pub check_scattered_inherent_impl: bool,
205    /// Per-path configuration overrides keyed by glob pattern.
206    #[serde(default)]
207    pub overrides: BTreeMap<Box<str>, PathOverride>,
208}
209
210/// Per-path overrides (e.g., for `tests/**`). `None` inherits from base config.
211#[derive(Debug, Deserialize, Default)]
212#[serde(deny_unknown_fields)]
213pub struct PathOverride {
214    /// `Some(false)` disables all checks for matched paths.
215    pub enabled: Option<bool>,
216    /// Replace nesting depth limit.
217    pub max_depth: Option<usize>,
218    /// Replace maximum parameter count.
219    pub max_params: Option<usize>,
220    /// Replace function body line ceiling.
221    pub max_function_body_lines: Option<usize>,
222    /// Replace the `large-source-file` warning line ceiling. Pair with a
223    /// TOML comment recording why this path is allowed to be large.
224    pub source_file_warn_lines: Option<usize>,
225    /// Replace the `large-source-file` denial line ceiling.
226    pub source_file_deny_lines: Option<usize>,
227    /// Replace the `high-method-count` method ceiling.
228    pub max_methods: Option<usize>,
229    /// Replace forbidden attribute patterns.
230    pub forbid_attributes: Option<PatternOverride>,
231    /// Replace forbidden type patterns.
232    pub forbid_types: Option<PatternOverride>,
233    /// Replace forbidden call patterns.
234    pub forbid_calls: Option<PatternOverride>,
235    /// Replace forbidden macro patterns.
236    pub forbid_macros: Option<PatternOverride>,
237    /// Replace generic naming thresholds.
238    pub check_naming: Option<NamingOverride>,
239    /// Replace nested-if check state.
240    pub check_nested_if: Option<bool>,
241    /// Replace if-in-match check state.
242    pub check_if_in_match: Option<bool>,
243    /// Replace nested-match check state.
244    pub check_nested_match: Option<bool>,
245    /// Replace match-in-if check state.
246    pub check_match_in_if: Option<bool>,
247    /// Replace else-chain check state.
248    pub check_else_chain: Option<bool>,
249    /// Replace `else` keyword ban state.
250    pub forbid_else: Option<bool>,
251    /// Replace `unsafe` block ban state.
252    pub forbid_unsafe: Option<bool>,
253    /// Replace dyn-return check state.
254    pub check_dyn_return: Option<bool>,
255    /// Replace dyn-param check state.
256    pub check_dyn_param: Option<bool>,
257    /// Replace `Vec<Box<dyn T>>` check state.
258    pub check_vec_box_dyn: Option<bool>,
259    /// Replace dyn-field check state.
260    pub check_dyn_field: Option<bool>,
261    /// Replace clone-in-loop check state.
262    pub check_clone_in_loop: Option<bool>,
263    /// Replace default-hasher check state.
264    pub check_default_hasher: Option<bool>,
265    /// Replace mixed-concerns check state.
266    pub check_mixed_concerns: Option<bool>,
267    /// Replace inline-tests check state.
268    pub check_inline_tests: Option<bool>,
269    /// Replace let-underscore-result check state.
270    pub check_let_underscore_result: Option<bool>,
271    /// Replace high-param-count check state.
272    pub check_high_param_count: Option<bool>,
273    /// Replace long-function-body check state.
274    pub check_long_function_body: Option<bool>,
275    /// Replace module-root-definitions check state.
276    pub check_module_root_definitions: Option<bool>,
277    /// Replace large-source-file check state.
278    pub check_large_source_file: Option<bool>,
279    /// Replace high-method-count check state.
280    pub check_high_method_count: Option<bool>,
281    /// Replace the forwarder-counting policy.
282    pub count_forwarders: Option<bool>,
283    /// Replace item-visibility-policy check state.
284    pub check_item_visibility_policy: Option<bool>,
285    /// Replace ungated-test-api check state.
286    pub check_ungated_test_api: Option<bool>,
287    /// Replace conflicting-module-root check state.
288    pub check_conflicting_module_root: Option<bool>,
289    /// Replace flat-module-family check state.
290    pub check_flat_module_family: Option<bool>,
291    /// Replace feature-boundary check state.
292    pub check_feature_boundary: Option<bool>,
293    /// Replace scattered-inherent-impl check state.
294    pub check_scattered_inherent_impl: Option<bool>,
295}
296
297pub(super) fn default_max_depth() -> usize {
298    3
299}
300
301pub(super) fn default_else_chain_threshold() -> usize {
302    3
303}
304
305pub(super) fn default_max_params() -> usize {
306    5
307}
308
309pub(super) fn default_max_function_body_lines() -> usize {
310    120
311}
312
313static MODULE_ROOT_FILES_ARC: LazyLock<Arc<[Arc<str>]>> =
314    LazyLock::new(|| [Arc::from("mod.rs"), Arc::from("lib.rs")].into());
315
316pub(super) fn default_module_root_files() -> Arc<[Arc<str>]> {
317    Arc::clone(&MODULE_ROOT_FILES_ARC)
318}
319
320pub(super) fn default_source_file_warn_lines() -> usize {
321    500
322}
323
324pub(super) fn default_source_file_deny_lines() -> usize {
325    1000
326}
327
328pub(super) fn default_max_methods() -> usize {
329    40
330}
331
332static TEST_API_PATTERNS_ARC: LazyLock<Arc<[Arc<str>]>> =
333    LazyLock::new(|| [Arc::from("*_for_tests")].into());
334
335pub(super) fn default_test_api_patterns() -> Arc<[Arc<str>]> {
336    Arc::clone(&TEST_API_PATTERNS_ARC)
337}
338
339pub(super) fn default_test_support_feature() -> Box<str> {
340    "test-support".into()
341}
342
343fn default_true() -> bool {
344    true
345}