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