#![allow(dead_code)]
use crate::graph::style::NodeStyle;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
TopDown,
BottomTop,
LeftRight,
RightLeft,
}
impl Direction {
pub(crate) fn from_str(s: &str) -> Option<Self> {
match s.to_uppercase().as_str() {
"TD" | "TB" => Some(Direction::TopDown),
"BT" => Some(Direction::BottomTop),
"LR" => Some(Direction::LeftRight),
"RL" => Some(Direction::RightLeft),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ShapeSpec {
Rectangle(String),
Round(String),
Stadium(String),
Subroutine(String),
Cylinder(String),
Document(String),
Documents(String),
TaggedDocument(String),
Card(String),
TaggedRect(String),
Diamond(String),
Hexagon(String),
Trapezoid(String),
InvTrapezoid(String),
Parallelogram(String),
InvParallelogram(String),
ManualInput(String),
Asymmetric(String),
Circle(String),
DoubleCircle(String),
SmallCircle(String),
FramedCircle(String),
CrossedCircle(String),
TextBlock(String),
ForkJoin(String),
}
impl ShapeSpec {
pub fn text(&self) -> &str {
match self {
ShapeSpec::Rectangle(s)
| ShapeSpec::Round(s)
| ShapeSpec::Stadium(s)
| ShapeSpec::Subroutine(s)
| ShapeSpec::Cylinder(s)
| ShapeSpec::Document(s)
| ShapeSpec::Documents(s)
| ShapeSpec::TaggedDocument(s)
| ShapeSpec::Card(s)
| ShapeSpec::TaggedRect(s)
| ShapeSpec::Diamond(s)
| ShapeSpec::Hexagon(s)
| ShapeSpec::Trapezoid(s)
| ShapeSpec::InvTrapezoid(s)
| ShapeSpec::Parallelogram(s)
| ShapeSpec::InvParallelogram(s)
| ShapeSpec::ManualInput(s)
| ShapeSpec::Asymmetric(s)
| ShapeSpec::Circle(s)
| ShapeSpec::DoubleCircle(s)
| ShapeSpec::SmallCircle(s)
| ShapeSpec::FramedCircle(s)
| ShapeSpec::CrossedCircle(s)
| ShapeSpec::TextBlock(s)
| ShapeSpec::ForkJoin(s) => s,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Vertex {
pub id: String,
pub shape: Option<ShapeSpec>,
}
impl Vertex {
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
shape: None,
}
}
pub fn with_shape(id: impl Into<String>, shape: ShapeSpec) -> Self {
Self {
id: id.into(),
shape: Some(shape),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StrokeSpec {
Solid,
Dotted,
Thick,
Invisible,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArrowHead {
None,
Normal,
Cross,
Circle,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ConnectorSpec {
pub stroke: StrokeSpec,
pub left: ArrowHead,
pub right: ArrowHead,
pub length: usize,
pub label: Option<String>,
}
impl ConnectorSpec {
pub fn label(&self) -> Option<&str> {
self.label.as_deref()
}
pub fn has_arrow(&self) -> bool {
self.left != ArrowHead::None || self.right != ArrowHead::None
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EdgeSpec {
pub from: Vertex,
pub connector: ConnectorSpec,
pub to: Vertex,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SubgraphSpec {
pub id: String,
pub title: String,
pub statements: Vec<Statement>,
pub dir: Option<Direction>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NodeStyleStatement {
pub node_id: String,
pub style: NodeStyle,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Statement {
Vertex(Vertex),
Edge(EdgeSpec),
Subgraph(SubgraphSpec),
NodeStyle(NodeStyleStatement),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_subgraph_spec_construction() {
let sg = SubgraphSpec {
id: "sg1".to_string(),
title: "My Group".to_string(),
statements: vec![],
dir: None,
};
assert_eq!(sg.id, "sg1");
assert_eq!(sg.title, "My Group");
assert!(sg.statements.is_empty());
}
#[test]
fn test_statement_subgraph_variant() {
let sg = SubgraphSpec {
id: "sg1".to_string(),
title: "Title".to_string(),
statements: vec![Statement::Vertex(Vertex {
id: "A".to_string(),
shape: None,
})],
dir: None,
};
let stmt = Statement::Subgraph(sg);
match &stmt {
Statement::Subgraph(s) => {
assert_eq!(s.id, "sg1");
assert_eq!(s.statements.len(), 1);
}
_ => panic!("Expected Subgraph variant"),
}
}
}