archunit/common/matching/
pattern_spec.rsuse std::borrow::Cow;
use super::PatternTarget;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PatternExclusion {
source: String,
target: Option<PatternTarget>,
}
impl PatternExclusion {
fn new(source: impl Into<String>, target: Option<PatternTarget>) -> Self {
Self {
source: source.into(),
target,
}
}
#[must_use]
pub fn source(&self) -> &str {
&self.source
}
#[must_use]
pub const fn target(&self) -> Option<PatternTarget> {
self.target
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PatternSpec {
source: String,
exclusions: Vec<PatternExclusion>,
}
impl PatternSpec {
#[must_use]
pub fn new(source: impl Into<String>) -> Self {
Self {
source: source.into(),
exclusions: Vec::new(),
}
}
#[must_use]
pub fn except(self, exclusion: impl Into<String>) -> Self {
self.with_exclusion(exclusion, None)
}
#[must_use]
pub fn except_all<I, S>(mut self, exclusions: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.exclusions.extend(
exclusions
.into_iter()
.map(|source| PatternExclusion::new(source, None)),
);
self
}
#[must_use]
pub fn except_in_path(self, exclusion: impl Into<String>) -> Self {
self.with_exclusion(exclusion, Some(PatternTarget::Path))
}
#[must_use]
pub fn except_in_folder(self, exclusion: impl Into<String>) -> Self {
self.with_exclusion(exclusion, Some(PatternTarget::PathWithoutFilename))
}
#[must_use]
pub fn except_with_name(self, exclusion: impl Into<String>) -> Self {
self.with_exclusion(exclusion, Some(PatternTarget::Filename))
}
#[must_use]
pub fn except_for_types_matching(self, exclusion: impl Into<String>) -> Self {
self.with_exclusion(exclusion, Some(PatternTarget::TypeName))
}
#[must_use]
pub fn source(&self) -> &str {
&self.source
}
#[must_use]
pub fn exclusions(&self) -> &[PatternExclusion] {
&self.exclusions
}
fn with_exclusion(mut self, source: impl Into<String>, target: Option<PatternTarget>) -> Self {
self.exclusions.push(PatternExclusion::new(source, target));
self
}
}
impl From<&str> for PatternSpec {
fn from(source: &str) -> Self {
Self::new(source)
}
}
impl From<String> for PatternSpec {
fn from(source: String) -> Self {
Self::new(source)
}
}
impl From<&String> for PatternSpec {
fn from(source: &String) -> Self {
Self::new(source.clone())
}
}
impl From<Cow<'_, str>> for PatternSpec {
fn from(source: Cow<'_, str>) -> Self {
Self::new(source.into_owned())
}
}
#[must_use]
pub fn pattern(source: impl Into<String>) -> PatternSpec {
PatternSpec::new(source)
}
#[cfg(test)]
mod tests {
use super::pattern;
use crate::common::PatternTarget;
#[test]
fn exclusions_are_consuming_ordered_and_optionally_targeted() {
let base = pattern("src/**");
let configured = base
.clone()
.except_all(["src/generated/**", "src/vendor/**"])
.except_with_name("*_generated.rs")
.except_in_folder("src/fixtures/**")
.except_in_path("src/private/**")
.except_for_types_matching("Generated*");
assert!(base.exclusions().is_empty());
assert_eq!(configured.source(), "src/**");
assert_eq!(configured.exclusions().len(), 6);
assert_eq!(configured.exclusions()[0].target(), None);
assert_eq!(
configured.exclusions()[2].target(),
Some(PatternTarget::Filename)
);
assert_eq!(
configured.exclusions()[5].target(),
Some(PatternTarget::TypeName)
);
}
}