1use petgraph::{
5 prelude::*,
6 visit::{GraphProp, IntoEdgeReferences, IntoNodeReferences, NodeIndexable, NodeRef},
7};
8use std::fmt::{self, Write};
9
10static INDENT: &str = " ";
11
12pub trait DotVisitor<NR, ER> {
14 fn visit_node(&self, node: NR, f: &mut DotWrite<'_, '_>) -> fmt::Result;
17
18 fn visit_edge(&self, edge: ER, f: &mut DotWrite<'_, '_>) -> fmt::Result;
21
22 }
24
25#[derive(Copy, Clone, Debug)]
30#[allow(dead_code)]
31pub struct DisplayVisitor;
32
33impl<NR, ER> DotVisitor<NR, ER> for DisplayVisitor
34where
35 NR: NodeRef,
36 ER: EdgeRef,
37 NR::Weight: fmt::Display,
38 ER::Weight: fmt::Display,
39{
40 fn visit_node(&self, node: NR, f: &mut DotWrite<'_, '_>) -> fmt::Result {
41 write!(f, "{}", node.weight())
42 }
43
44 fn visit_edge(&self, edge: ER, f: &mut DotWrite<'_, '_>) -> fmt::Result {
45 write!(f, "{}", edge.weight())
46 }
47}
48
49impl<NR, ER, T> DotVisitor<NR, ER> for &T
50where
51 T: DotVisitor<NR, ER>,
52{
53 fn visit_node(&self, node: NR, f: &mut DotWrite<'_, '_>) -> fmt::Result {
54 (*self).visit_node(node, f)
55 }
56
57 fn visit_edge(&self, edge: ER, f: &mut DotWrite<'_, '_>) -> fmt::Result {
58 (*self).visit_edge(edge, f)
59 }
60}
61
62#[derive(Clone, Debug)]
63pub struct DotFmt<G, V> {
64 graph: G,
65 visitor: V,
66}
67
68impl<G, V> DotFmt<G, V>
69where
70 for<'a> &'a G: IntoEdgeReferences + IntoNodeReferences + GraphProp + NodeIndexable,
71 for<'a> V:
72 DotVisitor<<&'a G as IntoNodeReferences>::NodeRef, <&'a G as IntoEdgeReferences>::EdgeRef>,
73{
74 #[allow(dead_code)]
76 pub fn new(graph: G, visitor: V) -> Self {
77 Self { graph, visitor }
78 }
79
80 pub fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 writeln!(f, "{} {{", graph_type(&self.graph))?;
83
84 for node in self.graph.node_references() {
85 write!(
86 f,
87 "{}{} [label=\"",
88 INDENT,
89 (&self.graph).to_index(node.id())
90 )?;
91 self.visitor.visit_node(node, &mut DotWrite::new(f))?;
92 writeln!(f, "\"]")?;
93 }
94
95 let edge_str = edge_str(&self.graph);
96 for edge in self.graph.edge_references() {
97 write!(
98 f,
99 "{}{} {} {} [label=\"",
100 INDENT,
101 (&self.graph).to_index(edge.source()),
102 edge_str,
103 (&self.graph).to_index(edge.target())
104 )?;
105 self.visitor.visit_edge(edge, &mut DotWrite::new(f))?;
106 writeln!(f, "\"]")?;
107 }
108
109 writeln!(f, "}}")
110 }
111}
112
113impl<G, V> fmt::Display for DotFmt<G, V>
114where
115 for<'a> &'a G: IntoEdgeReferences + IntoNodeReferences + GraphProp + NodeIndexable,
116 for<'a> V:
117 DotVisitor<<&'a G as IntoNodeReferences>::NodeRef, <&'a G as IntoEdgeReferences>::EdgeRef>,
118{
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 self.fmt(f)
121 }
122}
123
124pub struct DotWrite<'a, 'b> {
126 f: &'a mut fmt::Formatter<'b>,
127 escape_backslashes: bool,
128}
129
130impl<'a, 'b> DotWrite<'a, 'b> {
131 fn new(f: &'a mut fmt::Formatter<'b>) -> Self {
132 Self {
133 f,
134 escape_backslashes: true,
135 }
136 }
137
138 #[allow(dead_code)]
142 pub fn set_escape_backslashes(&mut self, escape_backslashes: bool) {
143 self.escape_backslashes = escape_backslashes;
144 }
145
146 pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
154 Write::write_fmt(self, args)
156 }
157}
158
159impl Write for DotWrite<'_, '_> {
160 fn write_str(&mut self, s: &str) -> fmt::Result {
161 for c in s.chars() {
162 self.write_char(c)?;
163 }
164 Ok(())
165 }
166
167 fn write_char(&mut self, c: char) -> fmt::Result {
168 match c {
169 '"' => self.f.write_str(r#"\""#),
170 '\n' => self.f.write_str(r"\l"),
172 '\\' if self.escape_backslashes => self.f.write_str(r"\\"),
174 c => self.f.write_char(c),
176 }
177 }
178}
179
180fn graph_type<G: GraphProp>(graph: G) -> &'static str {
181 if graph.is_directed() {
182 "digraph"
183 } else {
184 "graph"
185 }
186}
187
188fn edge_str<G: GraphProp>(graph: G) -> &'static str {
189 if graph.is_directed() { "->" } else { "--" }
190}