diff --git a/src/bans.rs b/src/bans.rs index c2792159..8a668d84 100644 --- a/src/bans.rs +++ b/src/bans.rs @@ -177,6 +177,7 @@ pub fn check( let ValidConfig { file_id, denied, + denied_multiple_versions, allowed, skipped, multiple_versions, @@ -313,8 +314,13 @@ pub fn check( } } else if !tree_skipper.matches(krate, &mut pack) { if multi_detector.name != krate.name { - if multi_detector.dupes.len() > 1 && multiple_versions != LintLevel::Allow { - let severity = match multiple_versions { + let lint_level = match matches(&denied_multiple_versions, krate) { + Some(_) => LintLevel::Deny, + None => multiple_versions, + }; + + if multi_detector.dupes.len() > 1 && lint_level != LintLevel::Allow { + let severity = match lint_level { LintLevel::Warn => Severity::Warning, LintLevel::Deny => Severity::Error, LintLevel::Allow => unreachable!(), diff --git a/src/bans/cfg.rs b/src/bans/cfg.rs index f017c192..340b0b53 100644 --- a/src/bans/cfg.rs +++ b/src/bans/cfg.rs @@ -19,7 +19,7 @@ pub struct CrateId { #[derive(Deserialize, Clone)] #[cfg_attr(test, derive(Debug, PartialEq))] -#[serde(deny_unknown_fields)] +#[serde(rename_all = "kebab-case", deny_unknown_fields)] pub struct CrateBan { pub name: Spanned, #[serde(default = "any")] @@ -27,7 +27,11 @@ pub struct CrateBan { /// One or more crates that will allow this crate to be used if it is a /// direct dependency #[serde(default)] - pub wrappers: Vec>, + pub wrappers: Option>>>, + /// Setting this to true will only emit an error if multiple + // versions of the crate are found + #[serde(default)] + pub multiple_versions: Option>, } #[derive(Deserialize, Clone)] @@ -128,8 +132,15 @@ impl crate::cfg::UnvalidatedConfig for Config { ) }; - let denied: Vec<_> = self - .deny + let (deny_multiple_versions, deny): (Vec<_>, Vec<_>) = + self.deny.into_iter().partition(|kb| { + kb.multiple_versions + .as_ref() + .map(|spanned| spanned.value) + .unwrap_or(false) + }); + + let denied: Vec<_> = deny .into_iter() .map(|cb| KrateBan { id: Skrate::new( @@ -139,7 +150,42 @@ impl crate::cfg::UnvalidatedConfig for Config { }, cb.name.span, ), - wrappers: cb.wrappers, + wrappers: cb + .wrappers + .map(|spanned| spanned.value) + .unwrap_or_else(Vec::new), + }) + .collect(); + + let denied_multiple_versions: Vec<_> = deny_multiple_versions + .into_iter() + .map(|cb| { + let wrappers = cb.wrappers.filter(|spanned| !spanned.value.is_empty()); + if let Some(wrappers) = wrappers { + // cb.multiple_versions is guaranteed to be Some(_) by the + // earlier call to `partition` + let multiple_versions = cb.multiple_versions.unwrap(); + diags.push( + Diagnostic::error() + .with_message( + "a crate ban was specified with both `wrappers` and `multiple-versions`", + ) + .with_labels(vec![ + Label::secondary(cfg_file, wrappers.span) + .with_message("has one or more `wrappers`"), + Label::secondary(cfg_file, multiple_versions.span) + .with_message("has `multiple-versions` set to true"), + ]), + ); + } + + Skrate::new( + KrateId { + name: cb.name.value, + version: cb.version, + }, + cb.name.span, + ) }) .collect(); @@ -182,6 +228,7 @@ impl crate::cfg::UnvalidatedConfig for Config { multiple_versions: self.multiple_versions, highlight: self.highlight, denied, + denied_multiple_versions, allowed, skipped, wildcards: self.wildcards, @@ -212,6 +259,7 @@ pub struct ValidConfig { pub multiple_versions: LintLevel, pub highlight: GraphHighlight, pub(crate) denied: Vec, + pub(crate) denied_multiple_versions: Vec, pub(crate) allowed: Vec, pub(crate) skipped: Vec, pub(crate) tree_skipped: Vec>,