1#![doc = include_str!("readme.md")]
2use core::range::Range;
3
4#[derive(Debug, Clone, PartialEq)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7pub struct DotRoot {
8 pub graphs: Vec<Graph>,
10}
11
12impl std::fmt::Display for DotRoot {
13 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14 for graph in &self.graphs {
15 write!(f, "{}", graph)?;
16 }
17 Ok(())
18 }
19}
20
21#[derive(Debug, Clone, PartialEq)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub struct Graph {
25 pub strict: bool,
27 pub graph_type: GraphType,
29 pub id: Option<String>,
31 pub statements: Vec<Statement>,
33 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
35 pub span: Range<usize>,
36}
37
38impl std::fmt::Display for Graph {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 if self.strict {
41 write!(f, "strict ")?;
42 }
43 let gtype = match self.graph_type {
44 GraphType::Graph => "graph",
45 GraphType::Digraph => "digraph",
46 };
47 write!(f, "{} ", gtype)?;
48 if let Some(id) = &self.id {
49 write!(f, "{} ", id)?;
50 }
51 writeln!(f, "{{")?;
52 for stmt in &self.statements {
53 write!(f, " {}", stmt)?;
54 }
55 writeln!(f, "}}")?;
56 Ok(())
57 }
58}
59
60#[derive(Debug, Clone, PartialEq)]
62#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
63pub enum GraphType {
64 Graph,
66 Digraph,
68}
69
70#[derive(Debug, Clone, PartialEq)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73pub enum Statement {
74 Node(NodeStatement),
76 Edge(EdgeStatement),
78 Attribute(AttributeStatement),
80 Subgraph(SubgraphStatement),
82 Assignment(AssignmentStatement),
84}
85
86impl std::fmt::Display for Statement {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 match self {
89 Statement::Node(n) => {
90 write!(f, "{}", n.node_id.id)?;
91 if !n.attributes.is_empty() {
92 write!(f, " [")?;
93 for (i, attr) in n.attributes.iter().enumerate() {
94 if i > 0 {
95 write!(f, ", ")?;
96 }
97 write!(f, "{}", attr.name)?;
98 if let Some(val) = &attr.value {
99 write!(f, "={}", val)?;
100 }
101 }
102 write!(f, "]")?;
103 }
104 writeln!(f, ";")
105 }
106 Statement::Edge(e) => {
107 match &e.left {
108 EdgeOperand::Node(id) => write!(f, "{}", id.id)?,
109 EdgeOperand::Subgraph(s) => write!(f, "subgraph {} {{ ... }}", s.id.as_deref().unwrap_or(""))?,
110 }
111 for (op, target) in &e.edges {
112 let op_str = match op {
113 EdgeOp::Directed => "->",
114 EdgeOp::Undirected => "--",
115 };
116 match target {
117 EdgeOperand::Node(id) => write!(f, " {} {}", op_str, id.id)?,
118 EdgeOperand::Subgraph(s) => write!(f, " {} subgraph {} {{ ... }}", op_str, s.id.as_deref().unwrap_or(""))?,
119 }
120 }
121 if !e.attributes.is_empty() {
122 write!(f, " [")?;
123 for (i, attr) in e.attributes.iter().enumerate() {
124 if i > 0 {
125 write!(f, ", ")?;
126 }
127 write!(f, "{}", attr.name)?;
128 if let Some(val) = &attr.value {
129 write!(f, "={}", val)?;
130 }
131 }
132 write!(f, "]")?;
133 }
134 writeln!(f, ";")
135 }
136 Statement::Attribute(a) => {
137 let target = match a.target {
138 AttributeTarget::Graph => "graph",
139 AttributeTarget::Node => "node",
140 AttributeTarget::Edge => "edge",
141 };
142 write!(f, "{} [", target)?;
143 for (i, attr) in a.attributes.iter().enumerate() {
144 if i > 0 {
145 write!(f, ", ")?;
146 }
147 write!(f, "{}", attr.name)?;
148 if let Some(val) = &attr.value {
149 write!(f, "={}", val)?;
150 }
151 }
152 writeln!(f, "];")
153 }
154 Statement::Subgraph(s) => {
155 write!(f, "subgraph ")?;
156 if let Some(id) = &s.id {
157 write!(f, "{} ", id)?;
158 }
159 writeln!(f, "{{")?;
160 for stmt in &s.statements {
161 write!(f, " {}", stmt)?;
162 }
163 writeln!(f, " }}")
164 }
165 Statement::Assignment(a) => {
166 writeln!(f, "{}={};", a.id, a.value)
167 }
168 }
169 }
170}
171
172#[derive(Debug, Clone, PartialEq)]
174#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
175pub struct NodeStatement {
176 pub node_id: NodeId,
178 pub attributes: Vec<Attribute>,
180 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
182 pub span: Range<usize>,
183}
184
185#[derive(Debug, Clone, PartialEq)]
187#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
188pub struct EdgeStatement {
189 pub left: EdgeOperand,
191 pub edges: Vec<(EdgeOp, EdgeOperand)>,
193 pub attributes: Vec<Attribute>,
195 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
197 pub span: Range<usize>,
198}
199
200#[derive(Debug, Clone, PartialEq)]
202#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
203pub enum EdgeOperand {
204 Node(NodeId),
206 Subgraph(SubgraphStatement),
208}
209
210#[derive(Debug, Clone, PartialEq)]
212#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
213pub enum EdgeOp {
214 Directed, Undirected, }
219
220#[derive(Debug, Clone, PartialEq)]
222#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
223pub struct AttributeStatement {
224 pub target: AttributeTarget,
226 pub attributes: Vec<Attribute>,
228 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
230 pub span: Range<usize>,
231}
232
233#[derive(Debug, Clone, PartialEq)]
235#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
236pub enum AttributeTarget {
237 Graph,
239 Node,
241 Edge,
243}
244
245#[derive(Debug, Clone, PartialEq)]
247#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
248pub struct SubgraphStatement {
249 pub id: Option<String>,
251 pub statements: Vec<Statement>,
253 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
255 pub span: Range<usize>,
256}
257
258#[derive(Debug, Clone, PartialEq)]
260#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
261pub struct AssignmentStatement {
262 pub id: String,
264 pub value: String,
266 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
268 pub span: Range<usize>,
269}
270
271#[derive(Debug, Clone, PartialEq)]
273#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
274pub struct NodeId {
275 pub id: String,
277 pub port: Option<Port>,
279 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
281 pub span: Range<usize>,
282}
283
284#[derive(Debug, Clone, PartialEq)]
286#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
287pub struct Port {
288 pub id: Option<String>,
290 pub compass: Option<Compass>,
292 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
294 pub span: Range<usize>,
295}
296
297#[derive(Debug, Clone, PartialEq)]
299#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
300pub enum Compass {
301 N,
303 NE,
305 E,
307 SE,
309 S,
311 SW,
313 W,
315 NW,
317 C,
319}
320
321#[derive(Debug, Clone, PartialEq)]
323#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
324pub struct Attribute {
325 pub name: String,
327 pub value: Option<String>,
329 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
331 pub span: Range<usize>,
332}