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