pipecircuit_visualization/
lib.rs1mod 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;