petgraph/
dot.rs

1//! Simple graphviz dot file format output.
2
3use std::fmt::{self, Display, Write};
4
5use visit::{GraphRef};
6
7/// `Dot` implements output to graphviz .dot format for a graph.
8///
9/// Formatting and options are rather simple, this is mostly intended
10/// for debugging. Exact output may change.
11///
12/// # Examples
13///
14/// ```
15/// use petgraph::Graph;
16/// use petgraph::dot::{Dot, Config};
17///
18/// let mut graph = Graph::<_, ()>::new();
19/// graph.add_node("A");
20/// graph.add_node("B");
21/// graph.add_node("C");
22/// graph.add_node("D");
23/// graph.extend_with_edges(&[
24///     (0, 1), (0, 2), (0, 3),
25///     (1, 2), (1, 3),
26///     (2, 3),
27/// ]);
28///
29/// println!("{:?}", Dot::with_config(&graph, &[Config::EdgeNoLabel]));
30///
31/// // In this case the output looks like this:
32/// // 
33/// // digraph {
34/// //     0 [label="\"A\""]
35/// //     1 [label="\"B\""]
36/// //     2 [label="\"C\""]
37/// //     3 [label="\"D\""]
38/// //     0 -> 1
39/// //     0 -> 2
40/// //     0 -> 3
41/// //     1 -> 2
42/// //     1 -> 3
43/// //     2 -> 3
44/// // }
45///
46/// // If you need multiple config options, just list them all in the slice.
47/// ```
48pub 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    /// Create a `Dot` formatting wrapper with default configuration.
59    pub fn new(graph: G) -> Self {
60        Self::with_config(graph, &[])
61    }
62
63    /// Create a `Dot` formatting wrapper with custom configuration.
64    pub fn with_config(graph: G, config: &'a [Config]) -> Self {
65        Dot {
66            graph: graph,
67            config: config,
68        }
69    }
70}
71
72/// `Dot` configuration.
73///
74/// This enum does not have an exhaustive definition (will be expanded)
75#[derive(Debug, PartialEq, Eq)]
76pub enum Config {
77    /// Use indices for node labels.
78    NodeIndexLabel,
79    /// Use indices for edge labels.
80    EdgeIndexLabel,
81    /// Use no edge labels.
82    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        // output all labels
103        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        // output all edges
115        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
159/// Escape for Graphviz
160struct 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            // \l is for left justified linebreak
176            '\n' => return self.0.write_str(r#"\l"#),
177            _   => { }
178        }
179        self.0.write_char(c)
180    }
181}
182
183/// Pass Display formatting through a simple escaping filter
184struct 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
198/// Pass Debug formatting to Display
199struct 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}