use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Default)]
pub enum Direction {
#[default]
TD,
LR,
RL,
BT,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Shape {
Rect,
Rounded,
Stadium,
Diamond,
Circle,
DoubleCircle,
Cylinder,
Subroutine,
Hexagon,
Parallelogram,
ParallelogramAlt,
StateStart,
StateEnd,
ForkBar,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EdgeKind {
Arrow,
Open,
Dotted,
DottedOpen,
Thick,
ThickOpen,
Invisible,
}
impl EdgeKind {
pub fn has_arrow(self) -> bool {
matches!(self, EdgeKind::Arrow | EdgeKind::Dotted | EdgeKind::Thick)
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct NodeStyle {
pub fill: Option<String>,
pub stroke: Option<String>,
pub stroke_width: Option<f64>,
pub color: Option<String>,
}
impl NodeStyle {
pub fn apply_over(&mut self, over: &NodeStyle) {
if let Some(v) = &over.fill {
self.fill = Some(v.clone());
}
if let Some(v) = &over.stroke {
self.stroke = Some(v.clone());
}
if let Some(v) = over.stroke_width {
self.stroke_width = Some(v);
}
if let Some(v) = &over.color {
self.color = Some(v.clone());
}
}
}
#[derive(Debug, Clone)]
pub struct Node {
pub id: String,
pub label: String,
pub shape: Shape,
pub style: NodeStyle,
}
#[derive(Debug, Clone)]
pub struct Edge {
pub from: usize,
pub to: usize,
pub label: Option<String>,
pub kind: EdgeKind,
}
#[derive(Debug, Clone)]
pub struct Subgraph {
pub id: String,
pub title: String,
pub nodes: Vec<usize>,
pub parent: Option<usize>,
pub direction: Option<Direction>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum End {
Node(usize),
Sub(usize),
}
#[derive(Debug, Clone)]
pub struct SubEdge {
pub from: End,
pub to: End,
pub label: Option<String>,
pub kind: EdgeKind,
}
#[derive(Debug, Default)]
pub struct Graph {
pub direction: Direction,
pub nodes: Vec<Node>,
pub edges: Vec<Edge>,
pub subgraphs: Vec<Subgraph>,
pub sub_edges: Vec<SubEdge>,
index: HashMap<String, usize>,
}
impl Graph {
pub fn ensure_node(&mut self, id: &str, label: Option<String>, shape: Option<Shape>) -> usize {
if let Some(&i) = self.index.get(id) {
if let Some(l) = label {
self.nodes[i].label = l;
}
if let Some(s) = shape {
self.nodes[i].shape = s;
}
i
} else {
let i = self.nodes.len();
self.nodes.push(Node {
id: id.to_string(),
label: label.unwrap_or_else(|| id.to_string()),
shape: shape.unwrap_or(Shape::Rect),
style: NodeStyle::default(),
});
self.index.insert(id.to_string(), i);
i
}
}
pub fn add_edge(&mut self, from: usize, to: usize, label: Option<String>, kind: EdgeKind) {
self.edges.push(Edge {
from,
to,
label,
kind,
});
}
pub fn node_index(&self, id: &str) -> Option<usize> {
self.index.get(id).copied()
}
}
#[derive(Debug)]
pub enum Document {
Flowchart(Graph),
Er(ErDiagram),
Class(ClassDiagram),
Sequence(SequenceDiagram),
Pie(PieChart),
State(Graph),
Mindmap(Mindmap),
Journey(Journey),
}
#[derive(Debug, Default)]
pub struct ClassDiagram {
pub classes: Vec<Class>,
pub relations: Vec<ClassRel>,
index: HashMap<String, usize>,
}
impl ClassDiagram {
pub fn ensure_class(&mut self, name: &str) -> usize {
if let Some(&i) = self.index.get(name) {
i
} else {
let i = self.classes.len();
self.classes.push(Class {
name: name.to_string(),
fields: Vec::new(),
methods: Vec::new(),
});
self.index.insert(name.to_string(), i);
i
}
}
pub fn class_index(&self, name: &str) -> Option<usize> {
self.index.get(name).copied()
}
}
#[derive(Debug)]
pub struct Class {
pub name: String,
pub fields: Vec<Member>,
pub methods: Vec<Member>,
}
#[derive(Debug)]
pub struct Member {
pub visibility: Visibility,
pub text: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Visibility {
Public, Private, Protected, Package, None,
}
impl Visibility {
pub fn glyph(self) -> &'static str {
match self {
Visibility::Public => "+",
Visibility::Private => "-",
Visibility::Protected => "#",
Visibility::Package => "~",
Visibility::None => "",
}
}
}
#[derive(Debug)]
pub struct ClassRel {
pub from: usize,
pub to: usize,
pub kind: RelKind,
pub dashed: bool,
pub from_card: Option<String>,
pub to_card: Option<String>,
pub label: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RelKind {
Inheritance,
Realization,
Composition,
Aggregation,
Association,
Dependency,
Link,
}
#[derive(Debug, Default)]
pub struct SequenceDiagram {
pub participants: Vec<Participant>,
pub items: Vec<SeqItem>,
pub autonumber: bool,
index: HashMap<String, usize>,
}
impl SequenceDiagram {
pub fn ensure_participant(&mut self, id: &str) -> usize {
if let Some(&i) = self.index.get(id) {
i
} else {
let i = self.participants.len();
self.participants.push(Participant {
id: id.to_string(),
label: id.to_string(),
actor: false,
});
self.index.insert(id.to_string(), i);
i
}
}
pub fn participant_index(&self, id: &str) -> Option<usize> {
self.index.get(id).copied()
}
}
#[derive(Debug)]
pub struct Participant {
pub id: String,
pub label: String,
pub actor: bool,
}
#[derive(Debug)]
pub enum SeqItem {
Message {
from: usize,
to: usize,
text: String,
dashed: bool,
head: SeqHead,
activate: bool,
deactivate: bool,
},
Note { side: NoteSide, text: String },
Activate(usize),
Deactivate(usize),
FrameStart { kind: FrameKind, label: String },
FrameElse { label: String },
FrameEnd,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SeqHead {
Filled,
Open,
Cross,
Async,
None,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NoteSide {
Over(usize, Option<usize>),
LeftOf(usize),
RightOf(usize),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameKind {
Loop,
Opt,
Alt,
Par,
}
impl FrameKind {
pub fn keyword(self) -> &'static str {
match self {
FrameKind::Loop => "loop",
FrameKind::Opt => "opt",
FrameKind::Alt => "alt",
FrameKind::Par => "par",
}
}
}
#[derive(Debug, Default)]
pub struct ErDiagram {
pub entities: Vec<Entity>,
pub relations: Vec<Relation>,
index: HashMap<String, usize>,
}
impl ErDiagram {
pub fn ensure_entity(&mut self, name: &str) -> usize {
if let Some(&i) = self.index.get(name) {
i
} else {
let i = self.entities.len();
self.entities.push(Entity {
name: name.to_string(),
attrs: Vec::new(),
});
self.index.insert(name.to_string(), i);
i
}
}
}
#[derive(Debug)]
pub struct Entity {
pub name: String,
pub attrs: Vec<Attr>,
}
#[derive(Debug)]
pub struct Attr {
pub ty: String,
pub name: String,
pub keys: Vec<Key>,
pub comment: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Key {
Pk,
Fk,
Uk,
}
impl Key {
pub fn tag(self) -> &'static str {
match self {
Key::Pk => "PK",
Key::Fk => "FK",
Key::Uk => "UK",
}
}
}
#[derive(Debug)]
pub struct Relation {
pub from: usize,
pub to: usize,
pub card_from: Card,
pub card_to: Card,
pub identifying: bool,
pub label: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Card {
One,
ZeroOne,
ZeroMany,
OneMany,
}
#[derive(Debug, Default)]
pub struct PieChart {
pub title: Option<String>,
pub show_data: bool,
pub slices: Vec<PieSlice>,
}
#[derive(Debug)]
pub struct PieSlice {
pub label: String,
pub value: f64,
}
#[derive(Debug, Default)]
pub struct Mindmap {
pub nodes: Vec<MindNode>,
}
#[derive(Debug, Clone)]
pub struct MindNode {
pub text: String,
pub shape: MindShape,
pub parent: Option<usize>,
pub children: Vec<usize>,
pub depth: usize,
pub branch: Option<usize>,
}
#[derive(Debug, Default)]
pub struct Journey {
pub title: Option<String>,
pub sections: Vec<JourneySection>,
pub actors: Vec<String>,
}
#[derive(Debug)]
pub struct JourneySection {
pub name: String,
pub tasks: Vec<JourneyTask>,
}
#[derive(Debug)]
pub struct JourneyTask {
pub name: String,
pub score: u8,
pub actors: Vec<usize>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum MindShape {
#[default]
Rounded,
Square,
Circle,
Hexagon,
Bang,
Cloud,
}