Skip to main content

causal_hub/io/dot/
mod.rs

1mod attributes;
2pub use attributes::*;
3
4mod parser;
5use std::sync::Arc;
6
7pub use parser::{DOT, DOTParser, DotIO};
8
9use crate::{
10    models::{DiGraph, Graph, HasLabels, UnGraph},
11    types::{Error, Result},
12};
13
14/// Convert a directed graph into the graph-agnostic DOT representation.
15impl From<&DiGraph> for DOT {
16    fn from(graph: &DiGraph) -> Self {
17        let labels: Vec<&String> = graph.labels().iter().collect();
18        let vertices = graph
19            .labels()
20            .iter()
21            .map(|l| (l.clone(), VertexAttributes::default()))
22            .collect();
23        let edges = graph
24            .edges()
25            .iter()
26            .map(|&(x, y)| {
27                (
28                    labels[x].clone(),
29                    labels[y].clone(),
30                    EdgeAttributes::default(),
31                )
32            })
33            .collect();
34        Self {
35            graph_type: "digraph".to_string(),
36            strict: false,
37            id: None,
38            graph_attributes: GraphAttributes::default(),
39            default_node_attributes: VertexAttributes::default(),
40            default_edge_attributes: EdgeAttributes::default(),
41            vertices,
42            edges,
43        }
44    }
45}
46
47/// Build a directed graph from the graph-agnostic DOT representation.
48impl TryFrom<DOT> for DiGraph {
49    type Error = Error;
50
51    fn try_from(dot: DOT) -> Result<Self> {
52        if dot.graph_type != "digraph" {
53            return Err(Error::InvalidParameter(
54                "dot graph type",
55                "expected 'digraph' for a directed graph",
56            ));
57        }
58        let labels: Vec<String> = dot.vertices.keys().cloned().collect();
59        let mut graph = DiGraph::empty(labels)?;
60        for (stats, t, _) in dot.edges {
61            let x = graph.label_to_index(&stats)?;
62            let y = graph.label_to_index(&t)?;
63            graph.add_edge(x, y)?;
64        }
65        Ok(graph)
66    }
67}
68
69/// Convert an undirected graph into the graph-agnostic DOT representation.
70impl From<&UnGraph> for DOT {
71    fn from(graph: &UnGraph) -> Self {
72        let labels: Vec<&String> = graph.labels().iter().collect();
73        let vertices = graph
74            .labels()
75            .iter()
76            .map(|l| (l.clone(), VertexAttributes::default()))
77            .collect();
78        let edges = graph
79            .edges()
80            .iter()
81            .map(|&(x, y)| {
82                (
83                    labels[x].clone(),
84                    labels[y].clone(),
85                    EdgeAttributes::default(),
86                )
87            })
88            .collect();
89        Self {
90            graph_type: "graph".to_string(),
91            strict: false,
92            id: None,
93            graph_attributes: GraphAttributes::default(),
94            default_node_attributes: VertexAttributes::default(),
95            default_edge_attributes: EdgeAttributes::default(),
96            vertices,
97            edges,
98        }
99    }
100}
101
102/// Build an undirected graph from the graph-agnostic DOT representation.
103impl TryFrom<DOT> for UnGraph {
104    type Error = Error;
105
106    fn try_from(dot: DOT) -> Result<Self> {
107        if dot.graph_type != "graph" {
108            return Err(Error::InvalidParameter(
109                "dot graph type",
110                "expected 'graph' for an undirected graph",
111            ));
112        }
113        let labels: Vec<String> = dot.vertices.keys().cloned().collect();
114        let mut graph = UnGraph::empty(labels)?;
115        for (stats, t, _) in dot.edges {
116            let x = graph.label_to_index(&stats)?;
117            let y = graph.label_to_index(&t)?;
118            graph.add_edge(x, y)?;
119        }
120        Ok(graph)
121    }
122}
123
124impl DotIO for DiGraph {
125    fn from_dot_string(dot: &str) -> Result<Self> {
126        DiGraph::try_from(DOT::from_string(dot)?)
127    }
128
129    fn to_dot_string(&self) -> Result<String> {
130        DOT::from(self).to_string_repr()
131    }
132
133    fn from_dot_file(path: &str) -> Result<Self> {
134        let string =
135            std::fs::read_to_string(path).map_err(|evidence| Error::Io(Arc::new(evidence)))?;
136        Self::from_dot_string(&string)
137    }
138
139    fn to_dot_file(&self, path: &str) -> Result<()> {
140        let string = self.to_dot_string()?;
141        std::fs::write(path, string).map_err(|evidence| Error::Io(Arc::new(evidence)))?;
142        Ok(())
143    }
144}
145
146impl DotIO for UnGraph {
147    fn from_dot_string(dot: &str) -> Result<Self> {
148        UnGraph::try_from(DOT::from_string(dot)?)
149    }
150
151    fn to_dot_string(&self) -> Result<String> {
152        DOT::from(self).to_string_repr()
153    }
154
155    fn from_dot_file(path: &str) -> Result<Self> {
156        let string =
157            std::fs::read_to_string(path).map_err(|evidence| Error::Io(Arc::new(evidence)))?;
158        Self::from_dot_string(&string)
159    }
160
161    fn to_dot_file(&self, path: &str) -> Result<()> {
162        let string = self.to_dot_string()?;
163        std::fs::write(path, string).map_err(|evidence| Error::Io(Arc::new(evidence)))?;
164        Ok(())
165    }
166}