stern4rust/rules/
directory_subfolder_count_rule.rs1use std::path::Path;
6
7use crate::finding::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_workspace(&self, files: &[SourceFile]) -> Vec<Offence> {
82 let tree = PackageTree::of(files);
83 tree.directories()
84 .iter()
85 .filter_map(|directory| {
86 let found = tree.subdirectories_of(directory).len();
87 (found > self.limit).then(|| self.offence(&tree, directory, found))
88 })
89 .collect()
90 }
91}