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,
}
#[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, Default)]
pub struct Graph {
pub direction: Direction,
pub nodes: Vec<Node>,
pub edges: Vec<Edge>,
pub subgraphs: Vec<Subgraph>,
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),
}
#[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,
}