use std::path::Path;
use crate::finding::model::package_tree::PackageTree;
use crate::reporting::offence::Offence;
use crate::rule::Rule;
use crate::source_file::SourceFile;
pub struct DirectorySubfolderCountRule {
limit: usize,
}
impl DirectorySubfolderCountRule {
pub const DEFAULT_LIMIT: usize = 5;
pub fn new(limit: usize) -> Self {
Self { limit }
}
fn offence(&self, tree: &PackageTree, directory: &Path, found: usize) -> Offence {
let subject = Self::shown(directory);
let carrier = tree
.registries_in(directory)
.first()
.map(|path| Self::shown(path))
.unwrap_or_else(|| subject.clone());
Offence::new(
&carrier,
1,
self.name(),
format!(
"{subject} holds {found} subfolders, more than the {} a directory may hold",
self.limit
),
format!(
"group the subfolders of {subject} so that no directory holds more than {}",
self.limit
),
)
.with_subject(&subject)
}
fn shown(path: &Path) -> String {
let shown = path.to_string_lossy().replace('\\', "/");
if shown.is_empty() || shown == "." {
return "the package root".to_string();
}
shown
}
}
impl Default for DirectorySubfolderCountRule {
fn default() -> Self {
Self::new(Self::DEFAULT_LIMIT)
}
}
impl Rule for DirectorySubfolderCountRule {
fn name(&self) -> &'static str {
"directory-subfolder-count"
}
fn check(&self, _file: &SourceFile) -> Vec<Offence> {
Vec::new()
}
fn check_workspace(&self, files: &[SourceFile]) -> Vec<Offence> {
let tree = PackageTree::of(files);
tree.directories()
.iter()
.filter_map(|directory| {
let found = tree.subdirectories_of(directory).len();
(found > self.limit).then(|| self.offence(&tree, directory, found))
})
.collect()
}
fn requirement(&self) -> Option<&'static str> {
None
}
fn is_configured(&self) -> bool {
true
}
}