dot_parser/
subgraph_free.rs

1//! This module represents a flat graph, i.e. a graph without subgraph.
2//! To flatten the graph, existing subgraphs are transformed into their individual nodes: e.g.
3//! the statement `a -> { b c }` is transformed into two statements: `a -> b` and `a -> c`.
4//! Edge attributes are cloned: therefore, a graph can be flattened only if the attribute type
5//! `A` implements `Clone`.
6pub use crate::ast::AttrList;
7pub use crate::ast::AttrStmt;
8pub use crate::ast::NodeID;
9pub use crate::ast::NodeStmt;
10pub use crate::ast::Port;
11use either::Either;
12use std::collections::HashSet;
13
14/// A graph, very similar to Ast::Graph, but can not contain subgraphs.
15pub struct Graph<A> {
16    /// Specifies if the `Graph` is strict or not. A "strict" graph must not
17    /// contain the same edge multiple times. Notice that, for undirected edge,
18    /// an edge from `A` to `B` and an edge from `B` to `A` are equals.
19    pub strict: bool,
20    /// Specifies if the `Graph` is directed.
21    pub is_digraph: bool,
22    /// The name of the `Graph`, if any.
23    pub name: Option<String>,
24    /// The statements that describe the graph.
25    pub stmts: StmtList<A>,
26}
27
28impl<A> Graph<A> {
29    /// Filter and map attributes. The main intended usage of this function is
30    /// to convert attributes as `&'a str` into an enum, e.g.
31    /// to convert `["label"="whatever", "color"="foo"]` into
32    /// `[Attr::Label(whatever), Attr::Color(foo)]`.
33    ///
34    /// To take into account non-standard attributes, the `Attr` enum has to be
35    /// provided by the user.
36    pub fn filter_map<B>(self, f: &dyn Fn(A) -> Option<B>) -> Graph<B> {
37        let new_stmts: StmtList<B> = self.stmts.filter_map_attr(f);
38        Graph {
39            strict: self.strict,
40            is_digraph: self.is_digraph,
41            name: self.name,
42            stmts: new_stmts,
43        }
44    }
45
46    /// Returns all `NodeID`s that appear in the graph.
47    pub fn get_node_ids(&self) -> HashSet<NodeID> {
48        self.stmts.get_node_ids()
49    }
50
51    /// Returns all [EdgeStmt]s that appear in the graph.
52    /// Notice that it does not recurse into nested subgraphs.
53    pub fn get_edges_stmts(self) -> Vec<EdgeStmt<A>> {
54        self.stmts.get_edges_stmts()
55    }
56}
57
58impl<A> From<crate::ast::Graph<A>> for Graph<A>
59where
60    A: Clone,
61{
62    fn from(g: crate::ast::Graph<A>) -> Self {
63        Graph {
64            strict: g.strict,
65            is_digraph: g.is_digraph,
66            name: g.name,
67            stmts: g.stmts.into(),
68        }
69    }
70}
71
72/// A list of statements. This corresponds to the `stmt_list` non-terminal of the
73/// grammar.
74#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
75pub struct StmtList<A> {
76    /// The list of statements.
77    pub stmts: Vec<Stmt<A>>,
78}
79
80impl<'a, A> StmtList<A> {
81    fn filter_map_attr<B>(self, f: &'a dyn Fn(A) -> Option<B>) -> StmtList<B> {
82        self.stmts
83            .into_iter()
84            .map(|stmt| stmt.filter_map_attr(f))
85            .collect()
86    }
87
88    fn get_node_ids(&self) -> HashSet<NodeID> {
89        let mut hs = HashSet::new();
90        for stmt in self {
91            hs = hs.union(&stmt.get_node_ids()).cloned().collect();
92        }
93        hs
94    }
95
96    /// Returns a clone of all the EdgeStmt contained in the list.
97    fn get_edges_stmts(self) -> Vec<EdgeStmt<A>> {
98        let mut v = Vec::new();
99        for stmt in self {
100            if let Some(edge) = stmt.get_edge() {
101                v.push(edge)
102            }
103        }
104        v
105    }
106}
107
108impl<A> IntoIterator for StmtList<A> {
109    type Item = Stmt<A>;
110    type IntoIter = std::vec::IntoIter<Self::Item>;
111
112    fn into_iter(self) -> Self::IntoIter {
113        self.stmts.into_iter()
114    }
115}
116
117impl<'a, A> IntoIterator for &'a StmtList<A> {
118    type Item = &'a Stmt<A>;
119    type IntoIter = std::slice::Iter<'a, Stmt<A>>;
120
121    fn into_iter(self) -> Self::IntoIter {
122        self.stmts.iter()
123    }
124}
125
126impl<A> FromIterator<Stmt<A>> for StmtList<A> {
127    fn from_iter<T>(iter: T) -> Self
128    where
129        T: IntoIterator<Item = Stmt<A>>,
130    {
131        Self {
132            stmts: iter.into_iter().collect(),
133        }
134    }
135}
136
137impl<A> From<crate::ast::StmtList<A>> for StmtList<A>
138where
139    A: Clone,
140{
141    fn from(stmts: crate::ast::StmtList<A>) -> Self {
142        let mut v = Vec::new();
143        for stmt in stmts {
144            match stmt {
145                crate::ast::Stmt::NodeStmt(n) => {
146                    v.push(Stmt::NodeStmt(n));
147                }
148                crate::ast::Stmt::EdgeStmt(e) => {
149                    let edges: Vec<EdgeStmt<A>> = e.into();
150                    for stmt in edges {
151                        v.push(Stmt::EdgeStmt(stmt));
152                    }
153                }
154                crate::ast::Stmt::AttrStmt(a) => {
155                    v.push(Stmt::AttrStmt(a));
156                }
157                crate::ast::Stmt::IDEq(s1, s2) => {
158                    v.push(Stmt::IDEq(s1, s2));
159                }
160                crate::ast::Stmt::Subgraph(graph) => {
161                    let stmts: StmtList<A> = graph.stmts.into();
162                    for stmt in stmts {
163                        v.push(stmt)
164                    }
165                }
166            }
167        }
168        Self { stmts: v }
169    }
170}
171
172/// A statement of the graph. This corresponds to the `stmt` non-terminal of the
173/// grammar.
174#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
175pub enum Stmt<A> {
176    /// A node statement.
177    NodeStmt(NodeStmt<A>),
178    /// An edge statement.
179    EdgeStmt(EdgeStmt<A>),
180    /// An attribute statement.
181    AttrStmt(AttrStmt<A>),
182    /// An alias statement.
183    IDEq(String, String),
184}
185
186impl<'a, A> Stmt<A> {
187    /// Convert a statement with attributes of type `A` into a statement with
188    /// attributes of type `B`.
189    fn filter_map_attr<B>(self, f: &'a dyn Fn(A) -> Option<B>) -> Stmt<B> {
190        match self {
191            Stmt::NodeStmt(node) => Stmt::NodeStmt(node.filter_map_attr(f)),
192            Stmt::EdgeStmt(edge) => Stmt::EdgeStmt(edge.filter_map_attr(f)),
193            Stmt::AttrStmt(attr) => Stmt::AttrStmt(attr.filter_map_attr(f)),
194            Stmt::IDEq(a, b) => Stmt::IDEq(a, b),
195        }
196    }
197
198    /// Returns true if `self` is a `NodeStmt` variant.
199    pub fn is_node_stmt(&self) -> bool {
200        matches!(self, Stmt::NodeStmt(_))
201    }
202
203    /// Returns `Some(&node)` if `&self` if a `&NodeStmt(node)`, and `None`
204    /// otherwise.
205    pub fn get_node_ref(&self) -> Option<&NodeStmt<A>> {
206        if let Stmt::NodeStmt(node) = self {
207            Some(node)
208        } else {
209            None
210        }
211    }
212
213    /// Returns `Some(node)` if `self` if a `NodeStmt(node)`, and `None`
214    /// otherwise.
215    pub fn get_node(self) -> Option<NodeStmt<A>> {
216        if let Stmt::NodeStmt(node) = self {
217            Some(node)
218        } else {
219            None
220        }
221    }
222
223    /// Returns true if `self` is a `EdgeStmt` variant.
224    pub fn is_edge_stmt(&self) -> bool {
225        matches!(self, Stmt::EdgeStmt(_))
226    }
227
228    /// Returns `Some(&edge)` if `&self` if a `&EdgeStmt(edge)`, and `None`
229    /// otherwise.
230    pub fn get_edge_ref(&self) -> Option<&EdgeStmt<A>> {
231        if let Stmt::EdgeStmt(edge) = self {
232            Some(edge)
233        } else {
234            None
235        }
236    }
237
238    /// Returns `Some(edge)` if `self` if a `EdgeStmt(edge)`, and `None`
239    /// otherwise.
240    pub fn get_edge(self) -> Option<EdgeStmt<A>> {
241        if let Stmt::EdgeStmt(edge) = self {
242            Some(edge)
243        } else {
244            None
245        }
246    }
247
248    /// Returns true if `self` is a `AttrStmt` variant.
249    pub fn is_attr_stmt(&self) -> bool {
250        matches!(self, Stmt::AttrStmt(_))
251    }
252
253    /// Returns `Some(&attr)` if `&self` if a `&AttrStmt(attr)`, and `None`
254    /// otherwise.
255    pub fn get_attr_ref(&self) -> Option<&AttrStmt<A>> {
256        if let Stmt::AttrStmt(attr) = self {
257            Some(attr)
258        } else {
259            None
260        }
261    }
262
263    /// Returns `Some(attr)` if `self` if a `AttrStmt(attr)`, and `None`
264    /// otherwise.
265    pub fn get_attr(self) -> Option<AttrStmt<A>> {
266        if let Stmt::AttrStmt(attr) = self {
267            Some(attr)
268        } else {
269            None
270        }
271    }
272
273    /// Returns true if `self` is a `IDEq` variant.
274    pub fn is_ideq_stmt(&self) -> bool {
275        matches!(self, Stmt::IDEq(..))
276    }
277
278    /// Returns `Some((&id1, &id2))` if `&self` if a `&IDEq(id1, id2)` and `None`
279    /// otherwise.
280    pub fn get_ideq_ref(&self) -> Option<(&str, &str)> {
281        if let Stmt::IDEq(id1, id2) = self {
282            Some((id1, id2))
283        } else {
284            None
285        }
286    }
287
288    /// Returns all `NodeID`s that appear in this statement.
289    fn get_node_ids(&self) -> HashSet<NodeID> {
290        match self {
291            Stmt::EdgeStmt(e) => e.get_node_ids(),
292            Stmt::NodeStmt(n) => HashSet::from_iter([n.get_node_id().clone()]),
293            _ => HashSet::new(),
294        }
295    }
296}
297
298/// The description of an edge. This corresponds to the `edge_stmt` non-terminal
299/// of the grammar.
300#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
301pub struct EdgeStmt<A> {
302    /// The origin of the edge.
303    pub from: NodeID,
304    /// The destination of the edge.
305    pub next: EdgeRHS,
306    /// The attributes of the edge.
307    pub attr: Option<AttrList<A>>,
308}
309
310impl<A> EdgeStmt<A> {
311    fn filter_map_attr<B>(self, f: &dyn Fn(A) -> Option<B>) -> EdgeStmt<B> {
312        EdgeStmt {
313            from: self.from,
314            next: self.next,
315            attr: self.attr.map(|a| a.filter_map_attr(f)),
316        }
317    }
318
319    fn get_node_ids(&self) -> HashSet<NodeID> {
320        let mut nexts = self.next.get_node_ids();
321        nexts.insert(self.from.clone());
322        nexts
323    }
324}
325
326impl<A> From<crate::ast::EdgeStmt<A>> for Vec<EdgeStmt<A>>
327where
328    A: Clone,
329{
330    fn from(edge: crate::ast::EdgeStmt<A>) -> Vec<EdgeStmt<A>> {
331        let edges = edge.flatten();
332        let mut v: Vec<EdgeStmt<A>> = Vec::new();
333
334        for edge in edges {
335            // `edge` has just one destination (`edge.next.next` is `None`), as it comes from a
336            // "flattened" set of edges.
337            let from = edge.from;
338            let to = edge.next.to;
339            let attr = edge.attr;
340
341            let (from_ids, mut extra_edges_from) = match from {
342                Either::Left(node_from) => (HashSet::from_iter([node_from]), Vec::new()),
343                Either::Right(subgraph) => {
344                    let g: Graph<A> = subgraph.into_graph(false, false).into();
345                    (g.get_node_ids(), g.get_edges_stmts())
346                }
347            };
348
349            let (to_ids, mut extra_edges_to) = match to {
350                Either::Left(node_from) => (HashSet::from_iter([node_from]), Vec::new()),
351                Either::Right(subgraph) => {
352                    let g: Graph<A> = subgraph.into_graph(false, false).into();
353                    (g.get_node_ids(), g.get_edges_stmts())
354                }
355            };
356
357            for from in from_ids {
358                for to in &to_ids {
359                    v.push(EdgeStmt {
360                        from: from.clone(),
361                        next: EdgeRHS {
362                            to: to.clone(),
363                            next: None,
364                        },
365                        attr: attr.clone(),
366                    });
367                }
368            }
369            v.append(&mut extra_edges_from);
370            v.append(&mut extra_edges_to);
371        }
372        v
373    }
374}
375
376/// The Right hand side of an edge description. This corresponds to the
377/// `EdgeRHS` non-terminal of the grammar.
378/// Notice that the grammar allows multiple EdgeRHS in sequence, to chain edges:
379/// `A -> B -> C`.
380/// Notice that, contrary to Ast::EdgeRHS, Ast::FlatGraph::EdgeRHS can not contain a subgraph.
381#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
382pub struct EdgeRHS {
383    /// The identifier of the destination of the edge.
384    pub to: NodeID,
385    /// A possible chained RHS.
386    pub next: Option<Box<EdgeRHS>>,
387}
388
389impl EdgeRHS {
390    fn get_node_ids(&self) -> HashSet<NodeID> {
391        let mut nexts: HashSet<NodeID> = self
392            .next
393            .as_ref()
394            .map(|n| n.get_node_ids())
395            .unwrap_or_default();
396        nexts.insert(self.to.clone());
397        nexts
398    }
399}