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