archunit/files/fluentapi/
match_pattern_file_condition_builder.rsuse crate::{
common::{Filter, PatternError, ProjectLocator, RegexFactory},
files::FileInfo,
};
use super::{
CustomFileCondition, DependOnExternalModuleConditionBuilder, DependOnFileConditionBuilder,
FileConditionBuilder, MatchPatternFileCondition,
};
#[derive(Debug, Clone)]
#[must_use = "a file mood has no effect until a predicate is selected and checked"]
pub struct MatchPatternFileConditionBuilder {
scope: FileConditionBuilder,
negated: bool,
}
impl MatchPatternFileConditionBuilder {
pub(super) const fn new(scope: FileConditionBuilder, negated: bool) -> Self {
Self { scope, negated }
}
pub const fn scope(&self) -> &FileConditionBuilder {
&self.scope
}
#[must_use]
pub const fn is_negated(&self) -> bool {
self.negated
}
#[must_use]
pub const fn project_locator(&self) -> &ProjectLocator {
self.scope.project_locator()
}
#[must_use]
pub fn filters(&self) -> &[Filter] {
self.scope.filters()
}
#[must_use]
pub const fn selector_error(&self) -> Option<&PatternError> {
self.scope.selector_error()
}
pub fn have_name(
self,
pattern: impl Into<crate::common::PatternSpec>,
) -> MatchPatternFileCondition {
let check_filter = RegexFactory::default().filename_matcher(pattern);
self.matching(check_filter)
}
pub fn be_in_folder(
self,
pattern: impl Into<crate::common::PatternSpec>,
) -> MatchPatternFileCondition {
let check_filter = RegexFactory::default().folder_matcher(pattern);
self.matching(check_filter)
}
pub fn be_in_path(
self,
pattern: impl Into<crate::common::PatternSpec>,
) -> MatchPatternFileCondition {
let check_filter = RegexFactory::default().path_matcher(pattern);
self.matching(check_filter)
}
pub fn depend_on_files(self) -> DependOnFileConditionBuilder {
DependOnFileConditionBuilder::new(self)
}
pub fn depend_on_external_modules(self) -> DependOnExternalModuleConditionBuilder {
DependOnExternalModuleConditionBuilder::new(self)
}
pub fn adhere_to<F>(self, predicate: F, message: impl Into<String>) -> CustomFileCondition
where
F: Fn(&FileInfo) -> bool + Send + Sync + 'static,
{
CustomFileCondition::new(self, predicate, message)
}
fn matching(self, check_filter: Result<Filter, PatternError>) -> MatchPatternFileCondition {
MatchPatternFileCondition::new(self, check_filter)
}
}
#[cfg(test)]
mod tests {
use std::path::Path;
use crate::files::project_files_in;
use super::MatchPatternFileConditionBuilder;
#[test]
fn carries_one_owned_scope_and_one_mood_flag() {
let scope = project_files_in("examples/layered")
.in_folder("src/**")
.with_name("*.rs");
let mood = MatchPatternFileConditionBuilder::new(scope, true);
assert!(mood.is_negated());
assert_eq!(
mood.project_locator().path(),
Some(Path::new("examples/layered"))
);
assert_eq!(mood.filters().len(), 2);
assert!(mood.selector_error().is_none());
}
#[test]
fn preserves_invalid_selector_diagnostics() {
let scope = project_files_in("examples/layered").in_path("src/[api");
let mood = MatchPatternFileConditionBuilder::new(scope, false);
assert!(!mood.is_negated());
assert_eq!(
mood.selector_error().map(|error| error.pattern()),
Some("src/[api")
);
}
#[test]
fn creates_all_three_predicates_with_the_shared_mood() {
let scope = project_files_in("examples/layered").in_path("src/**");
let named =
MatchPatternFileConditionBuilder::new(scope.clone(), false).have_name("*_service.rs");
let folder =
MatchPatternFileConditionBuilder::new(scope.clone(), true).be_in_folder("src/service");
let path = MatchPatternFileConditionBuilder::new(scope, false).be_in_path("src/**");
assert!(!named.is_negated());
assert!(folder.is_negated());
assert!(!path.is_negated());
assert_eq!(
named.check_filter().map(|filter| filter.target()),
Some(crate::common::PatternTarget::Filename)
);
assert_eq!(
folder.check_filter().map(|filter| filter.target()),
Some(crate::common::PatternTarget::PathWithoutFilename)
);
assert_eq!(
path.check_filter().map(|filter| filter.target()),
Some(crate::common::PatternTarget::Path)
);
}
}