fdg_sim/
dot.rs

1//!
2//! Here's an example graph:
3//!
4//! ``` ignore
5//! graph {
6//!     0 [ label = "8" ]
7//!     1 [ label = "3" ]
8//!     2 [ label = "4" ]
9//!     3 [ label = "5" ]
10//!     4 [ label = "1" ]
11//!     5 [ label = "7" ]
12//!     6 [ label = "6" ]
13//!     7 [ label = "2" ]
14//!     4 -- 7 [ ]
15//!     7 -- 1 [ ]
16//!     1 -- 2 [ ]
17//!     2 -- 4 [ ]
18//!     3 -- 6 [ ]
19//!     6 -- 5 [ ]
20//!     5 -- 0 [ ]
21//!     0 -- 3 [ ]
22//!     4 -- 3 [ ]
23//!     7 -- 6 [ ]
24//!     1 -- 5 [ ]
25//!     2 -- 0 [ ]
26//! }
27//! ```
28
29use std::{collections::HashMap, error::Error, fmt};
30
31use petgraph::{
32    dot::{Config, Dot},
33    graph::NodeIndex,
34    stable_graph::StableGraph,
35    visit::{EdgeRef, IntoEdgeReferences},
36    Undirected,
37};
38
39use crate::ForceGraph;
40
41/// Errors that can be returned by the functions in this module.
42#[derive(Clone, Debug)]
43pub enum DotParseError {
44    /// Logically, this should never happen.
45    IndexNotFound(String),
46}
47
48impl fmt::Display for DotParseError {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        match self {
51            Self::IndexNotFound(n) => write!(f, "Index for {n} was not found in the graph"),
52        }
53    }
54}
55
56impl Error for DotParseError {}
57
58/// Convert a [`ForceGraph`] to the DOT
59pub fn graph_to_dot<N, E>(graph: &ForceGraph<N, E>) -> Result<String, DotParseError> {
60    let mut new_graph: StableGraph<String, (), Undirected> = StableGraph::default();
61    let mut indices: HashMap<String, NodeIndex> = HashMap::new();
62
63    for idx in graph.node_indices() {
64        let node = &graph[idx];
65
66        indices.insert(node.name.clone(), new_graph.add_node(node.name.clone()));
67    }
68
69    for edge in graph.edge_references() {
70        let source = &graph[edge.source()].name;
71        let target = &graph[edge.target()].name;
72
73        let source_idx = match indices.get(source) {
74            Some(idx) => *idx,
75            None => return Err(DotParseError::IndexNotFound(source.clone())),
76        };
77
78        let target_idx = match indices.get(target) {
79            Some(idx) => *idx,
80            None => return Err(DotParseError::IndexNotFound(target.clone())),
81        };
82
83        new_graph.add_edge(source_idx, target_idx, ());
84    }
85
86    Ok(format!("{:?}", Dot::with_config(&new_graph, &[Config::EdgeNoLabel])).replace("\\\"", ""))
87}