stern4rust/
rule_registry.rs1use crate::reporting::offence::Offence;
6use crate::reporting::rule_explanation::RuleExplanation;
7use crate::rule::Rule;
8use crate::rules::layout::directory_file_count_rule::DirectoryFileCountRule;
9use crate::rules::layout::directory_subfolder_count_rule::DirectorySubfolderCountRule;
10use crate::rules::layout::module_registry_rule::ModuleRegistryRule;
11use crate::rules::layout::paired_test_file_rule::PairedTestFileRule;
12use crate::rules::layout::registry_completeness_rule::RegistryCompletenessRule;
13use crate::rules::layout::test_file_name_postfix_rule::TestFileNamePostfixRule;
14use crate::rules::layout::tests_layout_rule::TestsLayoutRule;
15use crate::rules::manifest::spdx_matches_manifest_rule::SpdxMatchesManifestRule;
16use crate::rules::manifest::workspace_dependencies_rule::WorkspaceDependenciesRule;
17use crate::rules::source::declared_by_name_rule::DeclaredByNameRule;
18use crate::rules::source::header_rule::HeaderRule;
19use crate::rules::source::imported_paths_rule::ImportedPathsRule;
20use crate::rules::source::ordered_imports_rule::OrderedImportsRule;
21use crate::rules::source::pure_traits_rule::PureTraitsRule;
22use crate::rules::source::readable_source_rule::ReadableSourceRule;
23use crate::rules::source::single_implemented_type_rule::SingleImplementedTypeRule;
24use crate::rules::source::test_free_source_rule::TestFreeSourceRule;
25use crate::rules::testing::arrange_act_assert_rule::ArrangeActAssertRule;
26use crate::rules::testing::test_file_structure_rule::TestFileStructureRule;
27use crate::rules::testing::test_naming_rule::TestNamingRule;
28use crate::rules::testing::tested_public_api_rule::TestedPublicApiRule;
29use crate::settings::config::Config;
30use crate::settings::rule_selection::RuleSelection;
31use crate::source_file::SourceFile;
32
33pub struct RuleRegistry {
40 rules: Vec<Box<dyn Rule>>,
41}
42
43impl RuleRegistry {
44 fn all(config: &Config) -> Vec<Box<dyn Rule>> {
53 vec![
54 Box::new(ReadableSourceRule::new()),
55 Box::new(ArrangeActAssertRule::new()),
56 Box::new(DeclaredByNameRule::new()),
57 Box::new(DirectoryFileCountRule::new(
58 config
59 .max_files_per_directory
60 .unwrap_or(DirectoryFileCountRule::DEFAULT_LIMIT),
61 )),
62 Box::new(DirectorySubfolderCountRule::new(
63 config
64 .max_subfolders_per_directory
65 .unwrap_or(DirectorySubfolderCountRule::DEFAULT_LIMIT),
66 )),
67 Box::new(ImportedPathsRule::new()),
68 Box::new(ModuleRegistryRule::new()),
69 Box::new(OrderedImportsRule::new()),
70 Box::new(PairedTestFileRule::new()),
71 Box::new(PureTraitsRule::new()),
72 Box::new(RegistryCompletenessRule::new()),
73 Box::new(SingleImplementedTypeRule::new()),
74 Box::new(SpdxMatchesManifestRule::new(
75 config.manifest_license.clone(),
76 )),
77 Box::new(TestFileNamePostfixRule::new()),
78 Box::new(TestFileStructureRule::new()),
79 Box::new(TestFreeSourceRule::new()),
80 Box::new(TestNamingRule::new()),
81 Box::new(TestedPublicApiRule::new()),
82 Box::new(TestsLayoutRule::new()),
83 Box::new(WorkspaceDependenciesRule::new(
84 config.workspace_dependencies.clone(),
85 )),
86 Box::new(HeaderRule::new(config.expected_header.clone())),
87 ]
88 }
89
90 pub fn from_config(config: &Config) -> Self {
91 let rules = Self::all(config)
92 .into_iter()
93 .filter(|rule| rule.is_configured() && config.selection.includes(rule.name()))
94 .collect();
95 Self { rules }
96 }
97
98 pub fn new(rules: Vec<Box<dyn Rule>>) -> Self {
99 Self { rules }
100 }
101
102 pub fn known_names() -> Vec<&'static str> {
106 Self::all(&Config::default())
107 .iter()
108 .map(|rule| rule.name())
109 .collect()
110 }
111
112 pub fn skipped_names(selection: &RuleSelection) -> Vec<&'static str> {
116 Self::known_names()
117 .into_iter()
118 .filter(|name| !selection.includes(name))
119 .collect()
120 }
121
122 pub fn unconfigured_names(&self, config: &Config) -> Vec<&'static str> {
127 self.unconfigured(config)
128 .into_iter()
129 .map(|(name, _)| name)
130 .collect()
131 }
132
133 pub fn unconfigured(&self, config: &Config) -> Vec<(&'static str, &'static str)> {
137 let applied = self.names();
138 Self::all(config)
139 .iter()
140 .filter(|rule| {
141 config.selection.includes(rule.name()) && !applied.contains(&rule.name())
142 })
143 .map(|rule| (rule.name(), rule.requirement().unwrap_or("not configured")))
144 .collect()
145 }
146
147 pub fn is_empty(&self) -> bool {
148 self.rules.is_empty()
149 }
150
151 pub fn names(&self) -> Vec<&'static str> {
152 self.rules.iter().map(|rule| rule.name()).collect()
153 }
154
155 pub fn explanations(&self) -> Vec<RuleExplanation> {
158 self.rules.iter().map(|rule| rule.explanation()).collect()
159 }
160
161 pub fn check(&self, file: &SourceFile) -> Vec<Offence> {
162 self.rules
163 .iter()
164 .flat_map(|rule| rule.check(file))
165 .collect()
166 }
167
168 pub fn check_workspace(&self, files: &[SourceFile]) -> Vec<Offence> {
171 self.rules
172 .iter()
173 .flat_map(|rule| rule.check_workspace(files))
174 .collect()
175 }
176}