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#[cfg(feature = "petgraph")]
157use petgraph::graph::Graph as PetGraph;
158#[cfg(feature = "petgraph")]
159impl<A> From<Graph<A>> for PetGraph<Node<A>, AList<A>> {
160    fn from(val: Graph<A>) -> Self {
161        let mut graph = PetGraph::new();
162        let mut node_indices = HashMap::new();
163        for node in val.nodes.set {
164            let ni = graph.add_node(node.1);
165            node_indices.insert(node.0, ni);
166        }
167        for edge in val.edges.set {
168            let from_ni = node_indices.get(&edge.from).unwrap();
169            let to_ni = node_indices.get(&edge.to).unwrap();
170            graph.add_edge(*from_ni, *to_ni, edge.attr);
171        }
172        graph
173    }
174}
175
176/// A single node of the graph.
177#[derive(Debug, Clone)]
178pub struct Node<A> {
179    /// The identifier of the node.
180    pub id: String,
181    /// The port of the node.
182    pub port: Option<Port>,
183    /// The attributes that apply to this node.
184    pub attr: AList<A>,
185}
186
187impl<A> From<NodeStmt<A>> for Node<A> {
188    fn from(stmt: NodeStmt<A>) -> Self {
189        Node {
190            id: stmt.node.id.to_string(),
191            port: stmt.node.port,
192            attr: stmt.attr.map(|list| list.into()).unwrap_or(AList::empty()),
193        }
194    }
195}
196
197impl<A> From<&NodeID> for Node<A> {
198    fn from(node: &NodeID) -> Self {
199        Node {
200            id: node.id.to_string(),
201            port: node.port.clone(),
202            attr: AList::empty(),
203        }
204    }
205}
206
207impl<A> Node<A> {
208    fn map<F, B>(self, f: F) -> Node<B>
209    where
210        F: Fn(A) -> Option<B>,
211    {
212        Node {
213            id: self.id,
214            port: self.port,
215            attr: self.attr.filter_map_attr(&f),
216        }
217    }
218}
219
220#[cfg(feature = "display")]
221impl<A> Display for Node<A>
222where
223    A: Display,
224{
225    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
226        write!(f, "\"{}\" ", self.id)?;
227        if let Some(port) = &self.port {
228            write!(f, "{}", port)?;
229        }
230        if !self.attr.is_empty() {
231            write!(f, "[{}]", self.attr)?;
232        }
233
234        Ok(())
235    }
236}
237
238/// A set of `Node`s.
239#[derive(Debug, Clone)]
240pub struct NodeSet<A> {
241    /// The set of nodes in the NodeSet. They are indexed by their identifier.
242    /// Note that this field being public is experimental.
243    pub set: HashMap<String, Node<A>>,
244}
245
246impl<A> NodeSet<A> {
247    fn insert_if_absent(&mut self, id: String, or: Node<A>) {
248        // TODO: clarify what happens if id != or.id
249        if self.set.get(&id).is_none() {
250            self.set.insert(id, or);
251        }
252    }
253
254    fn map<F, B>(self, f: F) -> NodeSet<B>
255    where
256        F: Fn(A) -> Option<B>,
257    {
258        let new_set = self
259            .set
260            .into_iter()
261            .map(|(name, node)| (name, node.map(&f)))
262            .collect();
263        NodeSet { set: new_set }
264    }
265}
266
267impl<A, I> From<I> for NodeSet<A>
268where
269    I: IntoIterator<Item = NodeStmt<A>>,
270{
271    fn from(nodes: I) -> Self {
272        let set: HashMap<_, _> = nodes
273            .into_iter()
274            .map(|node| (node.node.id.to_string(), node.into()))
275            .collect();
276        NodeSet { set }
277    }
278}
279
280#[cfg(feature = "display")]
281impl<A> Display for NodeSet<A>
282where
283    A: Display,
284{
285    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
286        for node in self.set.values() {
287            writeln!(f, "{}", node)?;
288        }
289
290        Ok(())
291    }
292}
293
294/// A set of `Edge`s.
295#[derive(Debug, Clone)]
296pub struct EdgeSet<A> {
297    /// `Edge`s of the set.
298    pub set: Vec<Edge<A>>,
299}
300
301impl<A> EdgeSet<A> {
302    fn empty() -> Self {
303        Self { set: Vec::new() }
304    }
305
306    fn map<F, B>(self, f: F) -> EdgeSet<B>
307    where
308        F: Fn(A) -> Option<B>,
309    {
310        let new_set = self.set.into_iter().map(|edge| edge.map(&f)).collect();
311        EdgeSet { set: new_set }
312    }
313}
314
315impl<A> AddAssign for EdgeSet<A> {
316    fn add_assign(&mut self, mut rhs: Self) {
317        self.set.append(&mut rhs.set)
318    }
319}
320
321#[cfg(feature = "display")]
322impl<A> Display for EdgeSet<A>
323where
324    A: Display,
325{
326    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
327        for edge in &self.set {
328            writeln!(f, "{}", edge)?;
329        }
330        Ok(())
331    }
332}
333
334/// A single edge of the graph.
335// TODO: replace Strings by a reference to the relevant NodeID
336#[derive(Debug, Clone, Eq, PartialEq)]
337pub struct Edge<A> {
338    /// The name of the origin of the edge.
339    pub from: String,
340    /// The name of the destination of the edge.
341    pub to: String,
342    /// A list of attributes that apply to this specific edge.
343    pub attr: AList<A>,
344}
345
346#[cfg(feature = "display")]
347impl<A> Display for Edge<A>
348where
349    A: Display,
350{
351    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
352        write!(f, "\"{}\" -> \"{}\" [{}]", self.from, self.to, self.attr)
353    }
354}
355
356impl<A> Edge<A> {
357    fn map<F, B>(self, f: F) -> Edge<B>
358    where
359        F: Fn(A) -> Option<B>,
360    {
361        Edge {
362            from: self.from,
363            to: self.to,
364            attr: self.attr.filter_map_attr(&f),
365        }
366    }
367}
368
369impl<A> From<(EdgeStmt<A>, &mut NodeSet<A>)> for EdgeSet<A>
370where
371    A: Clone,
372{
373    fn from(tuple: (EdgeStmt<A>, &mut NodeSet<A>)) -> Self {
374        let (stmt, nodes) = tuple;
375        let mut from = stmt.from;
376        let mut rhs = stmt.next;
377        let mut set = Vec::new();
378        let attr = stmt.attr.map(|list| list.into()).unwrap_or(AList::empty());
379
380        loop {
381            let to = rhs.to;
382            let from_id = from.id.clone();
383            let to_id = to.id.clone();
384
385            nodes.insert_if_absent(from.id.to_string(), (&from).into());
386            nodes.insert_if_absent(to.id.to_string(), (&to).into());
387
388            let edge = Edge {
389                from: from_id.to_string(),
390                to: to_id.to_string(),
391                attr: attr.clone(),
392            };
393
394            set.push(edge);
395
396            if rhs.next.is_none() {
397                return EdgeSet { set };
398            }
399            from = to;
400            rhs = *(rhs.next.unwrap());
401        }
402    }
403}
404
405impl<A, I> From<(I, &mut NodeSet<A>)> for EdgeSet<A>
406where
407    I: IntoIterator<Item = EdgeStmt<A>>,
408    A: Clone,
409{
410    fn from(tuple: (I, &mut NodeSet<A>)) -> Self {
411        let (stmts, nodes) = tuple;
412        let mut set = EdgeSet::empty();
413        for stmt in stmts {
414            set += (stmt, &mut *nodes).into();
415        }
416        set
417    }
418}
419
420#[derive(Debug, Copy, Clone)]
421/// An `AttrStmt`, i.e. a statement that applies to either the whole graph, all
422/// edges, or all nodes. Note that, in a canonical graph, `AttrStmt`s contain a
423/// single statement.
424pub enum AttrStmt<A> {
425    /// An `AttrStmt` that applies to the whole graph.
426    Graph(A),
427    /// An `AttrStmt` that applies to all nodes of the graph.
428    Node(A),
429    /// An `AttrStmt` that applies to all edges of the graph.
430    Edge(A),
431}
432
433impl<A> AttrStmt<A> {
434    fn filter_map<F, B>(self, f: F) -> Option<AttrStmt<B>>
435    where
436        F: Fn(A) -> Option<B>,
437    {
438        match self {
439            AttrStmt::Graph(a) => {
440                f(a).map(AttrStmt::Graph)
441            }
442            AttrStmt::Node(a) => {
443                f(a).map(AttrStmt::Node)
444            }
445            AttrStmt::Edge(a) => {
446                f(a).map(AttrStmt::Edge)
447            }
448        }
449    }
450}
451
452impl<A> From<AstAttrStmt<A>> for Vec<AttrStmt<A>> {
453    fn from(val: AstAttrStmt<A>) -> Self {
454        match val {
455            AstAttrStmt::Graph(list) => {
456                let alist: AList<A> = list.into();
457                alist
458                    .into_iter()
459                    .map(|attr| AttrStmt::Graph(attr))
460                    .collect()
461            }
462            AstAttrStmt::Node(list) => {
463                let alist: AList<A> = list.into();
464                alist
465                    .into_iter()
466                    .map(|attr| AttrStmt::Node(attr))
467                    .collect()
468            }
469            AstAttrStmt::Edge(list) => {
470                let alist: AList<A> = list.into();
471                alist
472                    .into_iter()
473                    .map(|attr| AttrStmt::Edge(attr))
474                    .collect()
475            }
476        }
477    }
478}
479
480#[cfg(feature = "display")]
481impl<A> Display for AttrStmt<A>
482where
483    A: Display,
484{
485    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
486        match self {
487            AttrStmt::Graph(attr) => write!(f, "graph [{}]", attr),
488            AttrStmt::Edge(attr) => write!(f, "edge [{}]", attr),
489            AttrStmt::Node(attr) => write!(f, "node [{}]", attr),
490        }
491    }
492}
493
494#[derive(Clone, Debug)]
495/// An identifier equality, i.e. a statement that has the form `ID '=' ID`.
496pub struct IDEq {
497    /// The left hand side ID,
498    pub lhs: String,
499    /// The right hand side ID,
500    pub rhs: String,
501}
502
503#[cfg(feature = "display")]
504impl Display for IDEq { 
505    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
506        write!(f, "{} = {}", self.lhs, self.rhs)
507    }
508}