mod behavior;
mod derive;
mod render;
use std::{error::Error, fmt, path::Path};
pub use behavior::{BehaviorCheck, BehaviorError, BehaviorInventory, CheckDisposition};
pub use render::DiagramTarget;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ArchitectureError {
message: String,
}
impl ArchitectureError {
fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
}
impl fmt::Display for ArchitectureError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.message)
}
}
impl Error for ArchitectureError {}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CrateArchitecture {
state_machines: Vec<StateMachineDiagram>,
}
impl CrateArchitecture {
pub fn read(crate_root: impl AsRef<Path>) -> Result<Self, ArchitectureError> {
derive::read_crate(crate_root.as_ref())
}
pub fn state_machines(&self) -> &[StateMachineDiagram] {
&self.state_machines
}
pub fn state_machine(&self, name: &str) -> Result<&StateMachineDiagram, ArchitectureError> {
let mut matches = self
.state_machines
.iter()
.filter(|state_machine| state_machine.name == name);
let first = matches.next().ok_or_else(|| {
ArchitectureError::new(format!("state machine `{name}` was not found"))
})?;
if matches.next().is_some() {
return Err(ArchitectureError::new(format!(
"state machine name `{name}` was ambiguous; use its qualified name"
)));
}
Ok(first)
}
pub fn state_machine_qualified(
&self,
qualified_name: &str,
) -> Result<&StateMachineDiagram, ArchitectureError> {
self.state_machines
.iter()
.find(|state_machine| state_machine.qualified_name == qualified_name)
.ok_or_else(|| {
ArchitectureError::new(format!("state machine `{qualified_name}` was not found"))
})
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StateMachineDiagram {
name: String,
qualified_name: String,
steps: Vec<StepDiagram>,
}
impl StateMachineDiagram {
pub fn name(&self) -> &str {
&self.name
}
pub fn qualified_name(&self) -> &str {
&self.qualified_name
}
pub fn steps(&self) -> &[StepDiagram] {
&self.steps
}
pub fn render(&self, target: DiagramTarget) -> String {
render::render(self, target)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StepDiagram {
input: String,
outcome: DiagramNode,
}
impl StepDiagram {
pub fn input(&self) -> &str {
&self.input
}
pub fn outcome(&self) -> &DiagramNode {
&self.outcome
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DiagramNode {
label: String,
children: Vec<DiagramNode>,
}
impl DiagramNode {
pub fn label(&self) -> &str {
&self.label
}
pub fn children(&self) -> &[DiagramNode] {
&self.children
}
}
#[cfg(test)]
mod tests;