archunit/files/fluentapi/
depend_on_file_condition_builder.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use crate::common::{Filter, PatternError, ProjectLocator, RegexFactory};

use super::{DependOnFileCondition, MatchPatternFileConditionBuilder};

/// Immutable object stage selecting internal dependency targets.
#[derive(Debug, Clone)]
#[must_use = "a dependency object stage has no effect until it is selected and checked"]
pub struct DependOnFileConditionBuilder {
    condition: MatchPatternFileConditionBuilder,
}

impl DependOnFileConditionBuilder {
    pub(super) const fn new(condition: MatchPatternFileConditionBuilder) -> Self {
        Self { condition }
    }

    /// Returns the subject scope and mood carried into this object stage.
    pub const fn condition(&self) -> &MatchPatternFileConditionBuilder {
        &self.condition
    }

    /// Returns where Cargo project discovery begins.
    #[must_use]
    pub const fn project_locator(&self) -> &ProjectLocator {
        self.condition.project_locator()
    }

    /// Returns the subject-file filters in chain order.
    #[must_use]
    pub fn subject_filters(&self) -> &[Filter] {
        self.condition.filters()
    }

    /// Returns whether matching object dependencies are forbidden rather than allowed.
    #[must_use]
    pub const fn is_negated(&self) -> bool {
        self.condition.is_negated()
    }

    /// Returns the first invalid subject-scope selector.
    #[must_use]
    pub const fn selector_error(&self) -> Option<&PatternError> {
        self.condition.selector_error()
    }

    /// Selects dependency targets whose final path segment matches `pattern`.
    pub fn with_name(
        self,
        pattern: impl Into<crate::common::PatternSpec>,
    ) -> DependOnFileCondition {
        let filter = RegexFactory::default().filename_matcher(pattern);
        DependOnFileCondition::new(self, filter)
    }

    /// Selects dependency targets whose containing folder matches `pattern`.
    pub fn in_folder(
        self,
        pattern: impl Into<crate::common::PatternSpec>,
    ) -> DependOnFileCondition {
        let filter = RegexFactory::default().folder_matcher(pattern);
        DependOnFileCondition::new(self, filter)
    }

    /// Selects dependency targets whose complete normalized path matches `pattern`.
    pub fn in_path(self, pattern: impl Into<crate::common::PatternSpec>) -> DependOnFileCondition {
        let filter = RegexFactory::default().path_matcher(pattern);
        DependOnFileCondition::new(self, filter)
    }
}

#[cfg(test)]
mod tests {
    use crate::{common::PatternTarget, files::project_files_in};

    #[test]
    fn enters_each_object_selector_with_the_subject_mood_intact() {
        let scope = project_files_in("examples/layered").in_folder("src/api");
        let named = scope
            .clone()
            .should()
            .depend_on_files()
            .with_name("*_service.rs");
        let folder = scope
            .clone()
            .should_not()
            .depend_on_files()
            .in_folder("src/service");
        let path = scope
            .should_not()
            .depend_on_files()
            .in_path("src/service/**");

        assert!(!named.is_negated());
        assert!(folder.is_negated());
        assert!(path.is_negated());
        assert_eq!(named.object_filters()[0].target(), PatternTarget::Filename);
        assert_eq!(
            folder.object_filters()[0].target(),
            PatternTarget::PathWithoutFilename
        );
        assert_eq!(path.object_filters()[0].target(), PatternTarget::Path);
    }
}