Skip to main content

pipecircuit_visualization/
lib.rs

1mod 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;