archunit/files/fluentapi/
depend_on_external_module_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
use crate::common::{Filter, PatternError, ProjectLocator, RegexFactory};

use super::{DependOnExternalModuleCondition, MatchPatternFileConditionBuilder};

/// Immutable object stage selecting external Cargo-visible crate names.
#[derive(Debug, Clone)]
#[must_use = "an external dependency stage has no effect until it is selected and checked"]
pub struct DependOnExternalModuleConditionBuilder {
    condition: MatchPatternFileConditionBuilder,
}

impl DependOnExternalModuleConditionBuilder {
    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 crate 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 external crate names matching `pattern`.
    pub fn matching(
        self,
        pattern: impl Into<crate::common::PatternSpec>,
    ) -> DependOnExternalModuleCondition {
        let filter = RegexFactory::default().path_matcher(pattern);
        DependOnExternalModuleCondition::new(self, filter)
    }
}

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

    #[test]
    fn enters_the_module_selector_with_the_subject_mood_intact() {
        let scope = project_files_in("examples/layered").in_folder("src/api");
        let positive = scope
            .clone()
            .should()
            .depend_on_external_modules()
            .matching("std");
        let negative = scope
            .should_not()
            .depend_on_external_modules()
            .matching("tokio");

        assert!(!positive.is_negated());
        assert!(negative.is_negated());
        assert_eq!(positive.module_filters().len(), 1);
        assert_eq!(positive.module_filters()[0].target(), PatternTarget::Path);
    }
}