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