archunit/slices/uml/
plantuml_diagram.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
use std::collections::BTreeSet;

use super::plantuml_dependency::validated_component_name;
use super::{PlantUmlDependency, PlantUmlError};

/// Immutable components and allowed directed dependencies parsed from PlantUML.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PlantUmlDiagram {
    /// Declared and dependency-implied component names in first-seen order.
    pub components: Vec<String>,
    /// Unique allowed dependencies in first-seen order.
    pub dependencies: Vec<PlantUmlDependency>,
}

impl PlantUmlDiagram {
    /// Creates a diagram, adding dependency endpoints to the component collection.
    pub fn new<C, D>(components: C, dependencies: D) -> Result<Self, PlantUmlError>
    where
        C: IntoIterator<Item = String>,
        D: IntoIterator<Item = PlantUmlDependency>,
    {
        let mut unique_dependencies = BTreeSet::new();
        let dependencies = dependencies
            .into_iter()
            .filter(|dependency| unique_dependencies.insert(dependency.clone()))
            .collect::<Vec<_>>();
        let mut names = Vec::new();
        let mut unique_names = BTreeSet::new();
        for component in components.into_iter().chain(
            dependencies
                .iter()
                .flat_map(|dependency| [dependency.source.clone(), dependency.target.clone()]),
        ) {
            let component = validated_component_name(component)?;
            if unique_names.insert(component.clone()) {
                names.push(component);
            }
        }

        Ok(Self {
            components: names,
            dependencies,
        })
    }

    /// Returns whether the diagram allows this exact directed dependency.
    #[must_use]
    pub fn allows(&self, source: &str, target: &str) -> bool {
        self.dependencies
            .iter()
            .any(|dependency| dependency.source == source && dependency.target == target)
    }
}