dot_parser/
canonical.rs

1//! This modules implement "Canonical graphs". This is motivated by the fact
2//! that graphs parsed naively may be difficult to work with, from a programming
3//! viewpoint: typically, `attr_list` in the grammar are defined as
4//! "[ID = ID, ...][ID = ID, ...]". Therefore, we may want to flatten such
5//! structures. This is done in the [`canonical`][crate::canonical] module.
6//!
7//! The main structure of the module is [`Graph`], which implements such a flatten
8//! graph.
9
10use std::collections::HashMap;
11use std::ops::AddAssign;
12
13#[cfg(feature = "display")]
14use std::fmt::{Display, Formatter};
15
16use crate::subgraph_free::{
17    AttrStmt as AstAttrStmt, EdgeStmt, Graph as SFGraph, NodeID, NodeStmt, Port, Stmt,
18};
19
20pub use crate::ast::AList;
21
22/// A `Graph` is a structure that can be created from a regular
23/// `Graph`, but that is more friendly to work with. For instance, in a `Graph`,
24/// attributes are most often given as a list of list of `Attr`, while in a
25/// `Graph`, the lists are flatten.
26#[derive(Debug, Clone)]
27pub struct Graph<A> {
28    /// Specifies if the `Graph` is strict or not. A "strict" graph must not
29    /// contain the same edge multiple times. Notice that, for undirected edge,
30    /// an edge from `A` to `B` and an edge from `B` to `A` are equals.
31    pub strict: bool,
32    /// Specifies if the `Graph` is directed.
33    pub is_digraph: bool,
34    /// The name of the `Graph`, if any.
35    pub name: Option<String>,
36    /// The global attributes of the graph.
37    pub attr: Vec<AttrStmt<A>>,
38    /// The nodes of the graph.
39    pub nodes: NodeSet<A>,
40    /// The edges of the graph.
41    pub edges: EdgeSet<A>,
42    /// The ID equalities declared in the graph.
43    pub ideqs: Vec<IDEq>,
44}
45
46#[cfg(feature = "display")]
47impl<A> Display for Graph<A>
48where
49    A: Display,
50{
51    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
52        if self.strict {
53            write!(f, "strict ")?;
54        }
55        if self.is_digraph {
56            write!(f, "digraph ")?;
57        } else {
58            write!(f, "graph ")?;
59        }
60        if let Some(name) = &self.name {
61            write!(f, "{}", name)?;
62        }
63
64        writeln!(f, "{{")?;
65        for stmt in &self.attr {
66            writeln!(f, "{}", stmt)?;
67        }
68
69        for ideq in &self.ideqs {
70            writeln!(f, "{}", ideq)?;
71        }
72
73        write!(f, "{}", self.nodes)?;
74        write!(f, "{}", self.edges)?;
75        writeln!(f, "}}")?;
76
77        Result::Ok(())
78    }
79}
80
81impl<A> Graph<A> {
82    /// Filter and map attributes. The main intended usage of this function is
83    /// to convert attributes as `&'a str` into an enum, e.g.
84    /// to convert `["label"="whatever", "color"="foo"]` into
85    /// `[Attr::Label(whatever), Attr::Color(foo)]`.
86    ///
87    /// To take into account non-standard attributes, the `Attr` enum has to be
88    /// provided by the user.
89    pub fn filter_map<F, B>(self, f: F) -> Graph<B>
90    where
91        F: Fn(A) -> Option<B>,
92    {
93        let new_attr = self
94            .attr
95            .into_iter()
96            .filter_map(|attr_stmt| attr_stmt.filter_map(&f))
97            .collect();
98        let new_nodes = self.nodes.map(&f);
99        let new_edges = self.edges.map(&f);
100
101        Graph {
102            strict: self.strict,
103            is_digraph: self.is_digraph,
104            name: self.name,
105            attr: new_attr,
106            nodes: new_nodes,
107            edges: new_edges,
108            ideqs: self.ideqs,
109        }
110    }
111}
112
113impl<A, G> From<G> for Graph<A>
114where
115    G: Into<SFGraph<A>>,
116    A: Clone,
117{
118    fn from(graph: G) -> Self {
119        let graph = graph.into();
120        let mut attrs: Vec<AstAttrStmt<_>> = Vec::new();
121        let mut nodes = Vec::new();
122        let mut edges = Vec::new();
123        let mut ideqs = Vec::new();
124
125        for stmt in graph.stmts {
126            match stmt {
127                Stmt::NodeStmt(node) => nodes.push(node),
128                Stmt::EdgeStmt(edge) => edges.push(edge),
129                Stmt::AttrStmt(attr) => attrs.push(attr),
130                Stmt::IDEq(lhs, rhs) => ideqs.push(IDEq {lhs, rhs}),
131            }
132        }
133
134        let mut nodes: NodeSet<A> = nodes.into();
135        let edges: EdgeSet<A> = (edges, &mut nodes).into();
136        let attr: Vec<AttrStmt<A>> = attrs
137            .into_iter()
138            .flat_map(|stmt: AstAttrStmt<_>| {
139                let attr_l: Vec<AttrStmt<A>> = stmt.into();
140                attr_l
141            })
142            .collect();
143
144        Graph {
145            strict: graph.strict,
146            is_digraph: graph.is_digraph,
147            name: graph.name,
148            attr,
149            nodes,
150            edges,
151            ideqs,
152        }
153    }
154}
155
156/// A single node of the graph.
157#[derive(Debug, Clone)]
158pub struct Node<A> {
159    /// The identifier of the node.
160    pub id: String,
161    /// The port of the node.
162    pub port: Option<Port>,
163    /// The attributes that apply to this node.
164    pub attr: AList<A>,
165}
166
167impl<A> From<NodeStmt<A>> for Node<A> {
168    fn from(stmt: NodeStmt<A>) -> Self {
169        Node {
170            id: stmt.node.id.to_string(),
171            port: stmt.node.port,
172            attr: stmt.attr.map(|list| list.into()).unwrap_or(AList::empty()),
173        }
174    }
175}
176
177impl<A> From<&NodeID> for Node<A> {
178    fn from(node: &NodeID) -> Self {
179        Node {
180            id: node.id.to_string(),
181            port: node.port.clone(),
182            attr: AList::empty(),
183        }
184    }
185}
186
187impl<A> Node<A> {
188    fn map<F, B>(self, f: F) -> Node<B>
189    where
190        F: Fn(A) -> Option<B>,
191    {
192        Node {
193            id: self.id,
194            port: self.port,
195            attr: self.attr.filter_map_attr(&f),
196        }
197    }
198}
199
200#[cfg(feature = "display")]
201impl<A> Display for Node<A>
202where
203    A: Display,
204{
205    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
206        write!(f, "\"{}\" ", self.id)?;
207        if let Some(port) = &self.port {
208            write!(f, "{}", port)?;
209        }
210        if !self.attr.is_empty() {
211            write!(f, "[{}]", self.attr)?;
212        }
213
214        Ok(())
215    }
216}
217
218/// A set of `Node`s.
219#[derive(Debug, Clone)]
220pub struct NodeSet<A> {
221    /// The set of nodes in the NodeSet. They are indexed by their identifier.
222    /// Note that this field being public is experimental.
223    pub set: HashMap<String, Node<A>>,
224}
225
226impl<A> NodeSet<A> {
227    fn insert_if_absent(&mut self, id: String, or: Node<A>) {
228        // TODO: clarify what happens if id != or.id
229        if self.set.get(&id).is_none() {
230            self.set.insert(id, or);
231        }
232    }
233
234    fn map<F, B>(self, f: F) -> NodeSet<B>
235    where
236        F: Fn(A) -> Option<B>,
237    {
238        let new_set = self
239            .set
240            .into_iter()
241            .map(|(name, node)| (name, node.map(&f)))
242            .collect();
243        NodeSet { set: new_set }
244    }
245}
246
247impl<A, I> From<I> for NodeSet<A>
248where
249    I: IntoIterator<Item = NodeStmt<A>>,
250{
251    fn from(nodes: I) -> Self {
252        let set: HashMap<_, _> = nodes
253            .into_iter()
254            .map(|node| (node.node.id.to_string(), node.into()))
255            .collect();
256        NodeSet { set }
257    }
258}
259
260#[cfg(feature = "display")]
261impl<A> Display for NodeSet<A>
262where
263    A: Display,
264{
265    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
266        for node in self.set.values() {
267            writeln!(f, "{}", node)?;
268        }
269
270        Ok(())
271    }
272}
273
274/// A set of `Edge`s.
275#[derive(Debug, Clone)]
276pub struct EdgeSet<A> {
277    /// `Edge`s of the set.
278    pub set: Vec<Edge<A>>,
279}
280
281impl<A> EdgeSet<A> {
282    fn empty() -> Self {
283        Self { set: Vec::new() }
284    }
285
286    fn map<F, B>(self, f: F) -> EdgeSet<B>
287    where
288        F: Fn(A) -> Option<B>,
289    {
290        let new_set = self.set.into_iter().map(|edge| edge.map(&f)).collect();
291        EdgeSet { set: new_set }
292    }
293}
294
295impl<A> AddAssign for EdgeSet<A> {
296    fn add_assign(&mut self, mut rhs: Self) {
297        self.set.append(&mut rhs.set)
298    }
299}
300
301#[cfg(feature = "display")]
302impl<A> Display for EdgeSet<A>
303where
304    A: Display,
305{
306    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
307        for edge in &self.set {
308            writeln!(f, "{}", edge)?;
309        }
310        Ok(())
311    }
312}
313
314/// A single edge of the graph.
315// TODO: replace Strings by a reference to the relevant NodeID
316#[derive(Debug, Clone, Eq, PartialEq)]
317pub struct Edge<A> {
318    /// The name of the origin of the edge.
319    pub from: String,
320    /// The name of the destination of the edge.
321    pub to: String,
322    /// A list of attributes that apply to this specific edge.
323    pub attr: AList<A>,
324}
325
326#[cfg(feature = "display")]
327impl<A> Display for Edge<A>
328where
329    A: Display,
330{
331    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
332        write!(f, "\"{}\" -> \"{}\" [{}]", self.from, self.to, self.attr)
333    }
334}
335
336impl<A> Edge<A> {
337    fn map<F, B>(self, f: F) -> Edge<B>
338    where
339        F: Fn(A) -> Option<B>,
340    {
341        Edge {
342            from: self.from,
343            to: self.to,
344            attr: self.attr.filter_map_attr(&f),
345        }
346    }
347}
348
349impl<A> From<(EdgeStmt<A>, &mut NodeSet<A>)> for EdgeSet<A>
350where
351    A: Clone,
352{
353    fn from(tuple: (EdgeStmt<A>, &mut NodeSet<A>)) -> Self {
354        let (stmt, nodes) = tuple;
355        let mut from = stmt.from;
356        let mut rhs = stmt.next;
357        let mut set = Vec::new();
358        let attr = stmt.attr.map(|list| list.into()).unwrap_or(AList::empty());
359
360        loop {
361            let to = rhs.to;
362            let from_id = from.id.clone();
363            let to_id = to.id.clone();
364
365            nodes.insert_if_absent(from.id.to_string(), (&from).into());
366            nodes.insert_if_absent(to.id.to_string(), (&to).into());
367
368            let edge = Edge {
369                from: from_id.to_string(),
370                to: to_id.to_string(),
371                attr: attr.clone(),
372            };
373
374            set.push(edge);
375
376            if rhs.next.is_none() {
377                return EdgeSet { set };
378            }
379            from = to;
380            rhs = *(rhs.next.unwrap());
381        }
382    }
383}
384
385impl<A, I> From<(I, &mut NodeSet<A>)> for EdgeSet<A>
386where
387    I: IntoIterator<Item = EdgeStmt<A>>,
388    A: Clone,
389{
390    fn from(tuple: (I, &mut NodeSet<A>)) -> Self {
391        let (stmts, nodes) = tuple;
392        let mut set = EdgeSet::empty();
393        for stmt in stmts {
394            set += (stmt, &mut *nodes).into();
395        }
396        set
397    }
398}
399
400#[derive(Debug, Copy, Clone)]
401/// An `AttrStmt`, i.e. a statement that applies to either the whole graph, all
402/// edges, or all nodes. Note that, in a canonical graph, `AttrStmt`s contain a
403/// single statement.
404pub enum AttrStmt<A> {
405    /// An `AttrStmt` that applies to the whole graph.
406    Graph(A),
407    /// An `AttrStmt` that applies to all nodes of the graph.
408    Node(A),
409    /// An `AttrStmt` that applies to all edges of the graph.
410    Edge(A),
411}
412
413impl<A> AttrStmt<A> {
414    fn filter_map<F, B>(self, f: F) -> Option<AttrStmt<B>>
415    where
416        F: Fn(A) -> Option<B>,
417    {
418        match self {
419            AttrStmt::Graph(a) => {
420                f(a).map(AttrStmt::Graph)
421            }
422            AttrStmt::Node(a) => {
423                f(a).map(AttrStmt::Node)
424            }
425            AttrStmt::Edge(a) => {
426                f(a).map(AttrStmt::Edge)
427            }
428        }
429    }
430}
431
432impl<A> From<AstAttrStmt<A>> for Vec<AttrStmt<A>> {
433    fn from(val: AstAttrStmt<A>) -> Self {
434        match val {
435            AstAttrStmt::Graph(list) => {
436                let alist: AList<A> = list.into();
437                alist
438                    .into_iter()
439                    .map(|attr| AttrStmt::Graph(attr))
440                    .collect()
441            }
442            AstAttrStmt::Node(list) => {
443                let alist: AList<A> = list.into();
444                alist
445                    .into_iter()
446                    .map(|attr| AttrStmt::Node(attr))
447                    .collect()
448            }
449            AstAttrStmt::Edge(list) => {
450                let alist: AList<A> = list.into();
451                alist
452                    .into_iter()
453                    .map(|attr| AttrStmt::Edge(attr))
454                    .collect()
455            }
456        }
457    }
458}
459
460#[cfg(feature = "display")]
461impl<A> Display for AttrStmt<A>
462where
463    A: Display,
464{
465    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
466        match self {
467            AttrStmt::Graph(attr) => write!(f, "graph [{}]", attr),
468            AttrStmt::Edge(attr) => write!(f, "edge [{}]", attr),
469            AttrStmt::Node(attr) => write!(f, "node [{}]", attr),
470        }
471    }
472}
473
474#[derive(Clone, Debug)]
475/// An identifier equality, i.e. a statement that has the form `ID '=' ID`.
476pub struct IDEq {
477    /// The left hand side ID,
478    pub lhs: String,
479    /// The right hand side ID,
480    pub rhs: String,
481}
482
483#[cfg(feature = "display")]
484impl Display for IDEq { 
485    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
486        write!(f, "{} = {}", self.lhs, self.rhs)
487    }
488}