Skip to main content

oak_dot/ast/
mod.rs

1#![doc = include_str!("readme.md")]
2use core::range::Range;
3
4/// Root node of a DOT file.
5#[derive(Debug, Clone, PartialEq)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7pub struct DotRoot {
8    /// The list of graphs defined in the DOT file.
9    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/// A graph definition (either a 'graph' or a 'digraph').
22#[derive(Debug, Clone, PartialEq)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub struct Graph {
25    /// Whether the graph is strict (no multiple edges between same nodes).
26    pub strict: bool,
27    /// The type of the graph (graph or digraph).
28    pub graph_type: GraphType,
29    /// Optional identifier for the graph.
30    pub id: Option<String>,
31    /// The list of statements within the graph.
32    pub statements: Vec<Statement>,
33    /// The source range of this graph definition.
34    #[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/// The type of a graph.
61#[derive(Debug, Clone, PartialEq)]
62#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
63pub enum GraphType {
64    /// An undirected graph.
65    Graph,
66    /// A directed graph.
67    Digraph,
68}
69
70/// A statement in a DOT graph.
71#[derive(Debug, Clone, PartialEq)]
72#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
73pub enum Statement {
74    /// A node definition or modification.
75    Node(NodeStatement),
76    /// An edge definition.
77    Edge(EdgeStatement),
78    /// A global attribute setting (graph, node, or edge).
79    Attribute(AttributeStatement),
80    /// A subgraph definition.
81    Subgraph(SubgraphStatement),
82    /// An assignment statement (key=value).
83    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/// A node statement.
173#[derive(Debug, Clone, PartialEq)]
174#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
175pub struct NodeStatement {
176    /// The ID of the node.
177    pub node_id: NodeId,
178    /// Attributes associated with the node.
179    pub attributes: Vec<Attribute>,
180    /// The source range of this node statement.
181    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
182    pub span: Range<usize>,
183}
184
185/// An edge statement.
186#[derive(Debug, Clone, PartialEq)]
187#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
188pub struct EdgeStatement {
189    /// The left-hand side operand of the edge.
190    pub left: EdgeOperand,
191    /// The list of edges following the left operand.
192    pub edges: Vec<(EdgeOp, EdgeOperand)>,
193    /// Attributes associated with the edge(s).
194    pub attributes: Vec<Attribute>,
195    /// The source range of this edge statement.
196    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
197    pub span: Range<usize>,
198}
199
200/// An operand in an edge statement.
201#[derive(Debug, Clone, PartialEq)]
202#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
203pub enum EdgeOperand {
204    /// A single node ID.
205    Node(NodeId),
206    /// A subgraph.
207    Subgraph(SubgraphStatement),
208}
209
210/// An edge operator.
211#[derive(Debug, Clone, PartialEq)]
212#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
213pub enum EdgeOp {
214    /// A directed edge operator (->).
215    Directed, // ->
216    /// An undirected edge operator (--).
217    Undirected, // --
218}
219
220/// An attribute statement.
221#[derive(Debug, Clone, PartialEq)]
222#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
223pub struct AttributeStatement {
224    /// The target of the attribute setting.
225    pub target: AttributeTarget,
226    /// The list of attributes to set.
227    pub attributes: Vec<Attribute>,
228    /// The source range of this attribute statement.
229    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
230    pub span: Range<usize>,
231}
232
233/// The target of an attribute statement.
234#[derive(Debug, Clone, PartialEq)]
235#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
236pub enum AttributeTarget {
237    /// Affects the graph.
238    Graph,
239    /// Affects all nodes.
240    Node,
241    /// Affects all edges.
242    Edge,
243}
244
245/// A subgraph statement.
246#[derive(Debug, Clone, PartialEq)]
247#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
248pub struct SubgraphStatement {
249    /// Optional identifier for the subgraph.
250    pub id: Option<String>,
251    /// The list of statements within the subgraph.
252    pub statements: Vec<Statement>,
253    /// The source range of this subgraph statement.
254    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
255    pub span: Range<usize>,
256}
257
258/// An assignment statement.
259#[derive(Debug, Clone, PartialEq)]
260#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
261pub struct AssignmentStatement {
262    /// The key (identifier).
263    pub id: String,
264    /// The value.
265    pub value: String,
266    /// The source range of this assignment.
267    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
268    pub span: Range<usize>,
269}
270
271/// A node identifier with optional port and compass point.
272#[derive(Debug, Clone, PartialEq)]
273#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
274pub struct NodeId {
275    /// The main identifier of the node.
276    pub id: String,
277    /// Optional port on the node.
278    pub port: Option<Port>,
279    /// The source range of this node ID.
280    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
281    pub span: Range<usize>,
282}
283
284/// A port on a node.
285#[derive(Debug, Clone, PartialEq)]
286#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
287pub struct Port {
288    /// Optional identifier for the port.
289    pub id: Option<String>,
290    /// Optional compass point.
291    pub compass: Option<Compass>,
292    /// The source range of this port definition.
293    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
294    pub span: Range<usize>,
295}
296
297/// Compass points for ports.
298#[derive(Debug, Clone, PartialEq)]
299#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
300pub enum Compass {
301    /// North.
302    N,
303    /// North-East.
304    NE,
305    /// East.
306    E,
307    /// South-East.
308    SE,
309    /// South.
310    S,
311    /// South-West.
312    SW,
313    /// West.
314    W,
315    /// North-West.
316    NW,
317    /// Center.
318    C,
319}
320
321/// A single attribute (key-value pair).
322#[derive(Debug, Clone, PartialEq)]
323#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
324pub struct Attribute {
325    /// The name of the attribute.
326    pub name: String,
327    /// The optional value of the attribute.
328    pub value: Option<String>,
329    /// The source range of this attribute.
330    #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
331    pub span: Range<usize>,
332}