Skip to main content

pipecircuit_visualization/
lib.rs

1mod program;
2pub use program::{InstallationDiagram, ProgramDiagram};
3mod behavior;
4mod derive;
5mod render;
6
7use std::{error::Error, fmt, path::Path};
8
9pub use behavior::{BehaviorCheck, BehaviorError, BehaviorInventory, CheckDisposition};
10pub use render::DiagramTarget;
11
12#[derive(Clone, Debug, Eq, PartialEq)]
13pub struct ArchitectureError {
14    message: String,
15}
16
17impl ArchitectureError {
18    fn new(message: impl Into<String>) -> Self {
19        Self {
20            message: message.into(),
21        }
22    }
23}
24
25impl fmt::Display for ArchitectureError {
26    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
27        formatter.write_str(&self.message)
28    }
29}
30
31impl Error for ArchitectureError {}
32
33#[derive(Clone, Debug, Eq, PartialEq)]
34pub struct CrateArchitecture {
35    state_machines: Vec<StateMachineDiagram>,
36}
37
38impl CrateArchitecture {
39    pub fn read(crate_root: impl AsRef<Path>) -> Result<Self, ArchitectureError> {
40        derive::read_crate(crate_root.as_ref())
41    }
42
43    pub fn state_machines(&self) -> &[StateMachineDiagram] {
44        &self.state_machines
45    }
46
47    pub fn state_machine(&self, name: &str) -> Result<&StateMachineDiagram, ArchitectureError> {
48        let mut matches = self
49            .state_machines
50            .iter()
51            .filter(|state_machine| state_machine.name == name);
52        let first = matches.next().ok_or_else(|| {
53            ArchitectureError::new(format!("state machine `{name}` was not found"))
54        })?;
55        if matches.next().is_some() {
56            return Err(ArchitectureError::new(format!(
57                "state machine name `{name}` was ambiguous; use its qualified name"
58            )));
59        }
60        Ok(first)
61    }
62
63    pub fn state_machine_qualified(
64        &self,
65        qualified_name: &str,
66    ) -> Result<&StateMachineDiagram, ArchitectureError> {
67        self.state_machines
68            .iter()
69            .find(|state_machine| state_machine.qualified_name == qualified_name)
70            .ok_or_else(|| {
71                ArchitectureError::new(format!("state machine `{qualified_name}` was not found"))
72            })
73    }
74}
75
76#[derive(Clone, Debug, Eq, PartialEq)]
77pub struct StateMachineDiagram {
78    name: String,
79    qualified_name: String,
80    steps: Vec<StepDiagram>,
81}
82
83impl StateMachineDiagram {
84    pub fn name(&self) -> &str {
85        &self.name
86    }
87
88    pub fn qualified_name(&self) -> &str {
89        &self.qualified_name
90    }
91
92    pub fn steps(&self) -> &[StepDiagram] {
93        &self.steps
94    }
95
96    pub fn render(&self, target: DiagramTarget) -> String {
97        render::render(self, target)
98    }
99}
100
101#[derive(Clone, Debug, Eq, PartialEq)]
102pub struct StepDiagram {
103    input: String,
104    outcome: DiagramNode,
105}
106
107impl StepDiagram {
108    pub fn input(&self) -> &str {
109        &self.input
110    }
111
112    pub fn outcome(&self) -> &DiagramNode {
113        &self.outcome
114    }
115}
116
117#[derive(Clone, Debug, Eq, PartialEq)]
118pub struct DiagramNode {
119    label: String,
120    children: Vec<DiagramNode>,
121}
122
123impl DiagramNode {
124    pub fn label(&self) -> &str {
125        &self.label
126    }
127
128    pub fn children(&self) -> &[DiagramNode] {
129        &self.children
130    }
131}
132
133#[cfg(test)]
134mod tests;