1use std::fmt::{self, Display, Write};
4
5use visit::{GraphRef};
6
7pub struct Dot<'a, G> {
49 graph: G,
50 config: &'a [Config],
51}
52
53static TYPE: [&'static str; 2] = ["graph", "digraph"];
54static EDGE: [&'static str; 2] = ["--", "->"];
55static INDENT: &'static str = " ";
56
57impl<'a, G> Dot<'a, G> where G: GraphRef {
58 pub fn new(graph: G) -> Self {
60 Self::with_config(graph, &[])
61 }
62
63 pub fn with_config(graph: G, config: &'a [Config]) -> Self {
65 Dot {
66 graph: graph,
67 config: config,
68 }
69 }
70}
71
72#[derive(Debug, PartialEq, Eq)]
76pub enum Config {
77 NodeIndexLabel,
79 EdgeIndexLabel,
81 EdgeNoLabel,
83 #[doc(hidden)]
84 _Incomplete(()),
85}
86
87use visit::{ IntoNodeReferences, NodeIndexable, IntoEdgeReferences, EdgeRef};
88use visit::{ Data, NodeRef, GraphProp, };
89
90impl<'a, G> Dot<'a, G>
91{
92 fn graph_fmt<NF, EF, NW, EW>(&self, g: G, f: &mut fmt::Formatter,
93 mut node_fmt: NF, mut edge_fmt: EF) -> fmt::Result
94 where G: NodeIndexable + IntoNodeReferences + IntoEdgeReferences,
95 G: GraphProp,
96 G: Data<NodeWeight=NW, EdgeWeight=EW>,
97 NF: FnMut(&NW, &mut FnMut(&Display) -> fmt::Result) -> fmt::Result,
98 EF: FnMut(&EW, &mut FnMut(&Display) -> fmt::Result) -> fmt::Result,
99 {
100 try!(writeln!(f, "{} {{", TYPE[g.is_directed() as usize]));
101
102 for node in g.node_references() {
104 try!(write!(f, "{}{}", INDENT, g.to_index(node.id())));
105 if self.config.contains(&Config::NodeIndexLabel) {
106 try!(writeln!(f, ""));
107 } else {
108 try!(write!(f, " [label=\""));
109 try!(node_fmt(node.weight(), &mut |d| Escaped(d).fmt(f)));
110 try!(writeln!(f, "\"]"));
111 }
112
113 }
114 for (i, edge) in g.edge_references().enumerate() {
116 try!(write!(f, "{}{} {} {}",
117 INDENT,
118 g.to_index(edge.source()),
119 EDGE[g.is_directed() as usize],
120 g.to_index(edge.target())));
121 if self.config.contains(&Config::EdgeNoLabel) {
122 try!(writeln!(f, ""));
123 } else if self.config.contains(&Config::EdgeIndexLabel) {
124 try!(writeln!(f, " [label=\"{}\"]", i));
125 } else {
126 try!(write!(f, " [label=\""));
127 try!(edge_fmt(edge.weight(), &mut |d| Escaped(d).fmt(f)));
128 try!(writeln!(f, "\"]"));
129 }
130 }
131
132 try!(writeln!(f, "}}"));
133 Ok(())
134 }
135}
136
137impl<'a, G> fmt::Display for Dot<'a, G>
138 where G: IntoEdgeReferences + IntoNodeReferences + NodeIndexable + GraphProp,
139 G::EdgeWeight: fmt::Display,
140 G::NodeWeight: fmt::Display,
141{
142 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
143 self.graph_fmt(self.graph, f, |n, cb| cb(n), |e, cb| cb(e))
144 }
145}
146
147impl<'a, G> fmt::Debug for Dot<'a, G>
148 where G: IntoEdgeReferences + IntoNodeReferences + NodeIndexable + GraphProp,
149 G::EdgeWeight: fmt::Debug,
150 G::NodeWeight: fmt::Debug,
151{
152 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
153 self.graph_fmt(self.graph, f,
154 |n, cb| cb(&DebugFmt(n)),
155 |e, cb| cb(&DebugFmt(e)))
156 }
157}
158
159struct Escaper<W>(W);
161
162impl<W> fmt::Write for Escaper<W>
163 where W: fmt::Write
164{
165 fn write_str(&mut self, s: &str) -> fmt::Result {
166 for c in s.chars() {
167 try!(self.write_char(c));
168 }
169 Ok(())
170 }
171
172 fn write_char(&mut self, c: char) -> fmt::Result {
173 match c {
174 '"' => try!(self.0.write_char('\\')),
175 '\n' => return self.0.write_str(r#"\l"#),
177 _ => { }
178 }
179 self.0.write_char(c)
180 }
181}
182
183struct Escaped<T>(T);
185
186impl<T> fmt::Display for Escaped<T>
187 where T: fmt::Display
188{
189 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
190 if f.alternate() {
191 write!(&mut Escaper(f), "{:#}\\l", &self.0)
192 } else {
193 write!(&mut Escaper(f), "{}", &self.0)
194 }
195 }
196}
197
198struct DebugFmt<T>(T);
200
201impl<T> fmt::Display for DebugFmt<T>
202 where T: fmt::Debug
203{
204 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205 self.0.fmt(f)
206 }
207}