use crate::finding::model::manifest_dependency::ManifestDependency;
use crate::reporting::offence::Offence;
use crate::reporting::rule_explanation::RuleExplanation;
use crate::rule::Rule;
use crate::source_file::SourceFile;
pub struct WorkspaceDependenciesRule {
declared: Option<Vec<ManifestDependency>>,
}
impl WorkspaceDependenciesRule {
pub fn new(declared: Option<Vec<ManifestDependency>>) -> Self {
Self { declared }
}
fn offence(&self, dependency: &ManifestDependency) -> Offence {
let name = &dependency.name;
Offence::new(
&dependency.manifest,
1,
self.name(),
format!(
"{} declares `{name}` in [{}] rather than taking it from the workspace",
dependency.manifest, dependency.section
),
format!(
"add `{name}` to [workspace.dependencies] in the root manifest, and write \
`{name} = {{ workspace = true }}` here"
),
)
.with_subject(name)
.with_expected(&format!("{name} = {{ workspace = true }}"))
}
}
impl Rule for WorkspaceDependenciesRule {
fn name(&self) -> &'static str {
"workspace-dependencies"
}
fn check(&self, _file: &SourceFile) -> Vec<Offence> {
Vec::new()
}
fn check_workspace(&self, _files: &[SourceFile]) -> Vec<Offence> {
self.declared
.iter()
.flatten()
.filter(|dependency| !dependency.takes_from_workspace)
.map(|dependency| self.offence(dependency))
.collect()
}
fn requirement(&self) -> Option<&'static str> {
None
}
fn is_configured(&self) -> bool {
true
}
fn explanation(&self) -> RuleExplanation {
RuleExplanation::new(
self.name(),
"A workspace declares its dependencies once, in the root, and every member takes them from there.",
"serde = { version = \"1\" } -- in a member manifest",
"serde.workspace = true",
)
}
}