use std::{
fmt::{self, Display},
rc::Rc,
str::FromStr,
};
use serde::{Deserialize, Serialize};
use crate::{color::Color, draw, semantic::element::Element};
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum DiagramKind {
Component,
Sequence,
}
impl Display for DiagramKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DiagramKind::Component => write!(f, "component"),
DiagramKind::Sequence => write!(f, "sequence"),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct Scope {
elements: Vec<Element>,
}
impl Scope {
pub fn new(elements: Vec<Element>) -> Self {
Self { elements }
}
pub fn elements(&self) -> &[Element] {
&self.elements
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum LayoutEngine {
#[cfg_attr(not(feature = "graphviz"), default)]
Basic,
Sugiyama,
#[cfg(feature = "graphviz")]
#[cfg_attr(feature = "graphviz", default)]
Graphviz,
}
impl FromStr for LayoutEngine {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"basic" => Ok(Self::Basic),
"sugiyama" => Ok(Self::Sugiyama),
#[cfg(feature = "graphviz")]
"graphviz" => Ok(Self::Graphviz),
_ => Err("Unsupported layout engine"),
}
}
}
impl From<LayoutEngine> for &'static str {
fn from(val: LayoutEngine) -> Self {
match val {
LayoutEngine::Basic => "basic",
LayoutEngine::Sugiyama => "sugiyama",
#[cfg(feature = "graphviz")]
LayoutEngine::Graphviz => "graphviz",
}
}
}
impl Display for LayoutEngine {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s: &'static str = (*self).into();
write!(f, "{s}")
}
}
#[derive(Debug, Clone)]
pub struct Diagram {
kind: DiagramKind,
scope: Scope,
layout_engine: LayoutEngine,
background_color: Option<Color>,
lifeline_definition: Option<Rc<draw::LifelineDefinition>>,
}
impl Diagram {
pub fn new(
kind: DiagramKind,
scope: Scope,
layout_engine: LayoutEngine,
background_color: Option<Color>,
lifeline_definition: Option<Rc<draw::LifelineDefinition>>,
) -> Self {
Self {
kind,
scope,
layout_engine,
background_color,
lifeline_definition,
}
}
pub fn kind(&self) -> DiagramKind {
self.kind
}
pub fn scope(&self) -> &Scope {
&self.scope
}
pub fn layout_engine(&self) -> LayoutEngine {
self.layout_engine
}
pub fn background_color(&self) -> Option<Color> {
self.background_color
}
pub fn lifeline_definition(&self) -> Option<&Rc<draw::LifelineDefinition>> {
self.lifeline_definition.as_ref()
}
}
#[derive(Debug, Clone)]
pub enum Block {
None,
Scope(Scope),
Diagram(Diagram),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_diagram_kind_display() {
assert_eq!(DiagramKind::Component.to_string(), "component");
assert_eq!(DiagramKind::Sequence.to_string(), "sequence");
}
#[test]
fn test_layout_engine_from_str() {
assert_eq!(
"basic".parse::<LayoutEngine>().unwrap(),
LayoutEngine::Basic
);
assert_eq!(
"sugiyama".parse::<LayoutEngine>().unwrap(),
LayoutEngine::Sugiyama
);
#[cfg(feature = "graphviz")]
assert_eq!(
"graphviz".parse::<LayoutEngine>().unwrap(),
LayoutEngine::Graphviz
);
let result: Result<LayoutEngine, _> = "invalid".parse();
assert!(result.is_err());
assert_eq!(result.unwrap_err(), "Unsupported layout engine");
}
#[test]
fn test_layout_engine_default() {
#[cfg(feature = "graphviz")]
assert_eq!(LayoutEngine::default(), LayoutEngine::Graphviz);
#[cfg(not(feature = "graphviz"))]
assert_eq!(LayoutEngine::default(), LayoutEngine::Basic);
}
#[test]
fn test_layout_engine_display() {
assert_eq!(LayoutEngine::Basic.to_string(), "basic");
assert_eq!(LayoutEngine::Sugiyama.to_string(), "sugiyama");
#[cfg(feature = "graphviz")]
assert_eq!(LayoutEngine::Graphviz.to_string(), "graphviz");
}
}