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