#![doc = include_str!("readme.md")]
use core::range::Range;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DotRoot {
pub graphs: Vec<Graph>,
}
impl std::fmt::Display for DotRoot {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for graph in &self.graphs {
write!(f, "{}", graph)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Graph {
pub strict: bool,
pub graph_type: GraphType,
pub id: Option<String>,
pub statements: Vec<Statement>,
#[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
pub span: Range<usize>,
}
impl std::fmt::Display for Graph {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.strict {
write!(f, "strict ")?;
}
let gtype = match self.graph_type {
GraphType::Graph => "graph",
GraphType::Digraph => "digraph",
};
write!(f, "{} ", gtype)?;
if let Some(id) = &self.id {
write!(f, "{} ", id)?;
}
writeln!(f, "{{")?;
for stmt in &self.statements {
write!(f, " {}", stmt)?;
}
writeln!(f, "}}")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum GraphType {
Graph,
Digraph,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Statement {
Node(NodeStatement),
Edge(EdgeStatement),
Attribute(AttributeStatement),
Subgraph(SubgraphStatement),
Assignment(AssignmentStatement),
}
impl std::fmt::Display for Statement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Statement::Node(n) => {
write!(f, "{}", n.node_id.id)?;
if !n.attributes.is_empty() {
write!(f, " [")?;
for (i, attr) in n.attributes.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", attr.name)?;
if let Some(val) = &attr.value {
write!(f, "={}", val)?;
}
}
write!(f, "]")?;
}
writeln!(f, ";")
}
Statement::Edge(e) => {
match &e.left {
EdgeOperand::Node(id) => write!(f, "{}", id.id)?,
EdgeOperand::Subgraph(s) => write!(f, "subgraph {} {{ ... }}", s.id.as_deref().unwrap_or(""))?,
}
for (op, target) in &e.edges {
let op_str = match op {
EdgeOp::Directed => "->",
EdgeOp::Undirected => "--",
};
match target {
EdgeOperand::Node(id) => write!(f, " {} {}", op_str, id.id)?,
EdgeOperand::Subgraph(s) => write!(f, " {} subgraph {} {{ ... }}", op_str, s.id.as_deref().unwrap_or(""))?,
}
}
if !e.attributes.is_empty() {
write!(f, " [")?;
for (i, attr) in e.attributes.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", attr.name)?;
if let Some(val) = &attr.value {
write!(f, "={}", val)?;
}
}
write!(f, "]")?;
}
writeln!(f, ";")
}
Statement::Attribute(a) => {
let target = match a.target {
AttributeTarget::Graph => "graph",
AttributeTarget::Node => "node",
AttributeTarget::Edge => "edge",
};
write!(f, "{} [", target)?;
for (i, attr) in a.attributes.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", attr.name)?;
if let Some(val) = &attr.value {
write!(f, "={}", val)?;
}
}
writeln!(f, "];")
}
Statement::Subgraph(s) => {
write!(f, "subgraph ")?;
if let Some(id) = &s.id {
write!(f, "{} ", id)?;
}
writeln!(f, "{{")?;
for stmt in &s.statements {
write!(f, " {}", stmt)?;
}
writeln!(f, " }}")
}
Statement::Assignment(a) => {
writeln!(f, "{}={};", a.id, a.value)
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NodeStatement {
pub node_id: NodeId,
pub attributes: Vec<Attribute>,
#[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
pub span: Range<usize>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct EdgeStatement {
pub left: EdgeOperand,
pub edges: Vec<(EdgeOp, EdgeOperand)>,
pub attributes: Vec<Attribute>,
#[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
pub span: Range<usize>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum EdgeOperand {
Node(NodeId),
Subgraph(SubgraphStatement),
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum EdgeOp {
Directed, Undirected, }
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AttributeStatement {
pub target: AttributeTarget,
pub attributes: Vec<Attribute>,
#[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
pub span: Range<usize>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AttributeTarget {
Graph,
Node,
Edge,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SubgraphStatement {
pub id: Option<String>,
pub statements: Vec<Statement>,
#[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
pub span: Range<usize>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AssignmentStatement {
pub id: String,
pub value: String,
#[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
pub span: Range<usize>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NodeId {
pub id: String,
pub port: Option<Port>,
#[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
pub span: Range<usize>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Port {
pub id: Option<String>,
pub compass: Option<Compass>,
#[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
pub span: Range<usize>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Compass {
N,
NE,
E,
SE,
S,
SW,
W,
NW,
C,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Attribute {
pub name: String,
pub value: Option<String>,
#[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
pub span: Range<usize>,
}