#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
LeftToRight,
TopToBottom,
RightToLeft,
BottomToTop,
}
impl Direction {
pub fn parse(s: &str) -> Option<Self> {
match s.to_uppercase().as_str() {
"LR" => Some(Self::LeftToRight),
"TD" | "TB" => Some(Self::TopToBottom),
"RL" => Some(Self::RightToLeft),
"BT" => Some(Self::BottomToTop),
_ => None,
}
}
pub fn is_horizontal(self) -> bool {
matches!(self, Self::LeftToRight | Self::RightToLeft)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum NodeShape {
#[default]
Rectangle,
Rounded,
Diamond,
Circle,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Node {
pub id: String,
pub label: String,
pub shape: NodeShape,
}
impl Node {
pub fn new(id: impl Into<String>, label: impl Into<String>, shape: NodeShape) -> Self {
Self {
id: id.into(),
label: label.into(),
shape,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Edge {
pub from: String,
pub to: String,
pub label: Option<String>,
}
impl Edge {
pub fn new(from: impl Into<String>, to: impl Into<String>, label: Option<String>) -> Self {
Self {
from: from.into(),
to: to.into(),
label,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Graph {
pub direction: Direction,
pub nodes: Vec<Node>,
pub edges: Vec<Edge>,
}
impl Graph {
pub fn new(direction: Direction) -> Self {
Self {
direction,
nodes: Vec::new(),
edges: Vec::new(),
}
}
pub fn node(&self, id: &str) -> Option<&Node> {
self.nodes.iter().find(|n| n.id == id)
}
pub fn has_node(&self, id: &str) -> bool {
self.nodes.iter().any(|n| n.id == id)
}
pub fn upsert_node(&mut self, node: Node) {
if let Some(existing) = self.nodes.iter_mut().find(|n| n.id == node.id) {
if existing.label == existing.id
&& (existing.shape == NodeShape::Rectangle)
&& (node.label != node.id || node.shape != NodeShape::Rectangle)
{
*existing = node;
}
} else {
self.nodes.push(node);
}
}
}