stern4rust/rules/layout/
directory_subfolder_count_rule.rs1use std::path::Path;
6
7use crate::finding::model::package_tree::PackageTree;
8use crate::reporting::offence::Offence;
9use crate::reporting::rule_explanation::RuleExplanation;
10use crate::rule::Rule;
11use crate::source_file::SourceFile;
12
13pub struct DirectorySubfolderCountRule {
29 limit: usize,
30}
31
32impl DirectorySubfolderCountRule {
33 pub const DEFAULT_LIMIT: usize = 5;
34
35 pub fn new(limit: usize) -> Self {
36 Self { limit }
37 }
38
39 fn offence(&self, tree: &PackageTree, directory: &Path, found: usize) -> Offence {
40 let subject = Self::shown(directory);
41 let carrier = tree
42 .registries_in(directory)
43 .first()
44 .map(|path| Self::shown(path))
45 .unwrap_or_else(|| subject.clone());
46 Offence::new(
47 &carrier,
48 1,
49 self.name(),
50 format!(
51 "{subject} holds {found} subfolders, more than the {} a directory may hold",
52 self.limit
53 ),
54 format!(
55 "group the subfolders of {subject} so that no directory holds more than {}",
56 self.limit
57 ),
58 )
59 .with_subject(&subject)
60 }
61
62 fn shown(path: &Path) -> String {
63 let shown = path.to_string_lossy().replace('\\', "/");
64 if shown.is_empty() || shown == "." {
65 return "the package root".to_string();
66 }
67 shown
68 }
69}
70
71impl Default for DirectorySubfolderCountRule {
72 fn default() -> Self {
73 Self::new(Self::DEFAULT_LIMIT)
74 }
75}
76
77impl Rule for DirectorySubfolderCountRule {
78 fn name(&self) -> &'static str {
79 "directory-subfolder-count"
80 }
81
82 fn check(&self, _file: &SourceFile) -> Vec<Offence> {
83 Vec::new()
84 }
85
86 fn check_workspace(&self, files: &[SourceFile]) -> Vec<Offence> {
87 let tree = PackageTree::of(files);
88 tree.directories()
89 .iter()
90 .filter_map(|directory| {
91 let found = tree.subdirectories_of(directory).len();
92 (found > self.limit).then(|| self.offence(&tree, directory, found))
93 })
94 .collect()
95 }
96
97 fn requirement(&self) -> Option<&'static str> {
98 None
99 }
100
101 fn is_configured(&self) -> bool {
102 true
103 }
104
105 fn explanation(&self) -> RuleExplanation {
106 RuleExplanation::new(
107 self.name(),
108 "A directory holds a number of subfolders a reader can hold in their head.",
109 "src/ -- 9 subfolders",
110 "src/ -- 4 subfolders, each grouping the rest",
111 )
112 }
113}