summaryrefslogtreecommitdiffstats
path: root/vendor/petgraph/src/dot.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/petgraph/src/dot.rs')
-rw-r--r--vendor/petgraph/src/dot.rs330
1 files changed, 330 insertions, 0 deletions
diff --git a/vendor/petgraph/src/dot.rs b/vendor/petgraph/src/dot.rs
new file mode 100644
index 000000000..fa31fdfe8
--- /dev/null
+++ b/vendor/petgraph/src/dot.rs
@@ -0,0 +1,330 @@
+//! Simple graphviz dot file format output.
+
+use std::fmt::{self, Display, Write};
+
+use crate::visit::{
+ Data, EdgeRef, GraphBase, GraphProp, GraphRef, IntoEdgeReferences, IntoNodeReferences,
+ NodeIndexable, NodeRef,
+};
+
+/// `Dot` implements output to graphviz .dot format for a graph.
+///
+/// Formatting and options are rather simple, this is mostly intended
+/// for debugging. Exact output may change.
+///
+/// # Examples
+///
+/// ```
+/// use petgraph::Graph;
+/// use petgraph::dot::{Dot, Config};
+///
+/// let mut graph = Graph::<_, ()>::new();
+/// graph.add_node("A");
+/// graph.add_node("B");
+/// graph.add_node("C");
+/// graph.add_node("D");
+/// graph.extend_with_edges(&[
+/// (0, 1), (0, 2), (0, 3),
+/// (1, 2), (1, 3),
+/// (2, 3),
+/// ]);
+///
+/// println!("{:?}", Dot::with_config(&graph, &[Config::EdgeNoLabel]));
+///
+/// // In this case the output looks like this:
+/// //
+/// // digraph {
+/// // 0 [label="\"A\""]
+/// // 1 [label="\"B\""]
+/// // 2 [label="\"C\""]
+/// // 3 [label="\"D\""]
+/// // 0 -> 1
+/// // 0 -> 2
+/// // 0 -> 3
+/// // 1 -> 2
+/// // 1 -> 3
+/// // 2 -> 3
+/// // }
+///
+/// // If you need multiple config options, just list them all in the slice.
+/// ```
+pub struct Dot<'a, G>
+where
+ G: IntoEdgeReferences + IntoNodeReferences,
+{
+ graph: G,
+ config: &'a [Config],
+ get_edge_attributes: &'a dyn Fn(G, G::EdgeRef) -> String,
+ get_node_attributes: &'a dyn Fn(G, G::NodeRef) -> String,
+}
+
+static TYPE: [&str; 2] = ["graph", "digraph"];
+static EDGE: [&str; 2] = ["--", "->"];
+static INDENT: &str = " ";
+
+impl<'a, G> Dot<'a, G>
+where
+ G: GraphRef + IntoEdgeReferences + IntoNodeReferences,
+{
+ /// Create a `Dot` formatting wrapper with default configuration.
+ pub fn new(graph: G) -> Self {
+ Self::with_config(graph, &[])
+ }
+
+ /// Create a `Dot` formatting wrapper with custom configuration.
+ pub fn with_config(graph: G, config: &'a [Config]) -> Self {
+ Self::with_attr_getters(graph, config, &|_, _| "".to_string(), &|_, _| {
+ "".to_string()
+ })
+ }
+
+ pub fn with_attr_getters(
+ graph: G,
+ config: &'a [Config],
+ get_edge_attributes: &'a dyn Fn(G, G::EdgeRef) -> String,
+ get_node_attributes: &'a dyn Fn(G, G::NodeRef) -> String,
+ ) -> Self {
+ Dot {
+ graph,
+ config,
+ get_edge_attributes,
+ get_node_attributes,
+ }
+ }
+}
+
+/// `Dot` configuration.
+///
+/// This enum does not have an exhaustive definition (will be expanded)
+#[derive(Debug, PartialEq, Eq)]
+pub enum Config {
+ /// Use indices for node labels.
+ NodeIndexLabel,
+ /// Use indices for edge labels.
+ EdgeIndexLabel,
+ /// Use no edge labels.
+ EdgeNoLabel,
+ /// Use no node labels.
+ NodeNoLabel,
+ /// Do not print the graph/digraph string.
+ GraphContentOnly,
+ #[doc(hidden)]
+ _Incomplete(()),
+}
+
+impl<'a, G> Dot<'a, G>
+where
+ G: GraphBase + IntoNodeReferences + IntoEdgeReferences,
+{
+ fn graph_fmt<NF, EF, NW, EW>(
+ &self,
+ g: G,
+ f: &mut fmt::Formatter,
+ mut node_fmt: NF,
+ mut edge_fmt: EF,
+ ) -> fmt::Result
+ where
+ G: NodeIndexable + IntoNodeReferences + IntoEdgeReferences,
+ G: GraphProp + GraphBase,
+ G: Data<NodeWeight = NW, EdgeWeight = EW>,
+ NF: FnMut(&NW, &mut dyn FnMut(&dyn Display) -> fmt::Result) -> fmt::Result,
+ EF: FnMut(&EW, &mut dyn FnMut(&dyn Display) -> fmt::Result) -> fmt::Result,
+ {
+ if !self.config.contains(&Config::GraphContentOnly) {
+ writeln!(f, "{} {{", TYPE[g.is_directed() as usize])?;
+ }
+
+ // output all labels
+ for node in g.node_references() {
+ write!(f, "{}{} [ ", INDENT, g.to_index(node.id()),)?;
+ if !self.config.contains(&Config::NodeNoLabel) {
+ write!(f, "label = \"")?;
+ if self.config.contains(&Config::NodeIndexLabel) {
+ write!(f, "{}", g.to_index(node.id()))?;
+ } else {
+ node_fmt(node.weight(), &mut |d| Escaped(d).fmt(f))?;
+ }
+ write!(f, "\" ")?;
+ }
+ writeln!(f, "{}]", (self.get_node_attributes)(g, node))?;
+ }
+ // output all edges
+ for (i, edge) in g.edge_references().enumerate() {
+ write!(
+ f,
+ "{}{} {} {} [ ",
+ INDENT,
+ g.to_index(edge.source()),
+ EDGE[g.is_directed() as usize],
+ g.to_index(edge.target()),
+ )?;
+ if !self.config.contains(&Config::EdgeNoLabel) {
+ write!(f, "label = \"")?;
+ if self.config.contains(&Config::EdgeIndexLabel) {
+ write!(f, "{}", i)?;
+ } else {
+ edge_fmt(edge.weight(), &mut |d| Escaped(d).fmt(f))?;
+ }
+ write!(f, "\" ")?;
+ }
+ writeln!(f, "{}]", (self.get_edge_attributes)(g, edge))?;
+ }
+
+ if !self.config.contains(&Config::GraphContentOnly) {
+ writeln!(f, "}}")?;
+ }
+ Ok(())
+ }
+}
+
+impl<'a, G> fmt::Display for Dot<'a, G>
+where
+ G: IntoEdgeReferences + IntoNodeReferences + NodeIndexable + GraphProp,
+ G::EdgeWeight: fmt::Display,
+ G::NodeWeight: fmt::Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.graph_fmt(self.graph, f, |n, cb| cb(n), |e, cb| cb(e))
+ }
+}
+
+impl<'a, G> fmt::Debug for Dot<'a, G>
+where
+ G: IntoEdgeReferences + IntoNodeReferences + NodeIndexable + GraphProp,
+ G::EdgeWeight: fmt::Debug,
+ G::NodeWeight: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.graph_fmt(
+ self.graph,
+ f,
+ |n, cb| cb(&DebugFmt(n)),
+ |e, cb| cb(&DebugFmt(e)),
+ )
+ }
+}
+
+/// Escape for Graphviz
+struct Escaper<W>(W);
+
+impl<W> fmt::Write for Escaper<W>
+where
+ W: fmt::Write,
+{
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ for c in s.chars() {
+ self.write_char(c)?;
+ }
+ Ok(())
+ }
+
+ fn write_char(&mut self, c: char) -> fmt::Result {
+ match c {
+ '"' | '\\' => self.0.write_char('\\')?,
+ // \l is for left justified linebreak
+ '\n' => return self.0.write_str("\\l"),
+ _ => {}
+ }
+ self.0.write_char(c)
+ }
+}
+
+/// Pass Display formatting through a simple escaping filter
+struct Escaped<T>(T);
+
+impl<T> fmt::Display for Escaped<T>
+where
+ T: fmt::Display,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if f.alternate() {
+ writeln!(&mut Escaper(f), "{:#}", &self.0)
+ } else {
+ write!(&mut Escaper(f), "{}", &self.0)
+ }
+ }
+}
+
+/// Pass Debug formatting to Display
+struct DebugFmt<T>(T);
+
+impl<T> fmt::Display for DebugFmt<T>
+where
+ T: fmt::Debug,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::{Config, Dot, Escaper};
+ use crate::prelude::Graph;
+ use crate::visit::NodeRef;
+ use std::fmt::Write;
+
+ #[test]
+ fn test_escape() {
+ let mut buff = String::new();
+ {
+ let mut e = Escaper(&mut buff);
+ let _ = e.write_str("\" \\ \n");
+ }
+ assert_eq!(buff, "\\\" \\\\ \\l");
+ }
+
+ fn simple_graph() -> Graph<&'static str, &'static str> {
+ let mut graph = Graph::<&str, &str>::new();
+ let a = graph.add_node("A");
+ let b = graph.add_node("B");
+ graph.add_edge(a, b, "edge_label");
+ graph
+ }
+
+ #[test]
+ fn test_nodeindexlable_option() {
+ let graph = simple_graph();
+ let dot = format!("{:?}", Dot::with_config(&graph, &[Config::NodeIndexLabel]));
+ assert_eq!(dot, "digraph {\n 0 [ label = \"0\" ]\n 1 [ label = \"1\" ]\n 0 -> 1 [ label = \"\\\"edge_label\\\"\" ]\n}\n");
+ }
+
+ #[test]
+ fn test_edgeindexlable_option() {
+ let graph = simple_graph();
+ let dot = format!("{:?}", Dot::with_config(&graph, &[Config::EdgeIndexLabel]));
+ assert_eq!(dot, "digraph {\n 0 [ label = \"\\\"A\\\"\" ]\n 1 [ label = \"\\\"B\\\"\" ]\n 0 -> 1 [ label = \"0\" ]\n}\n");
+ }
+
+ #[test]
+ fn test_edgenolable_option() {
+ let graph = simple_graph();
+ let dot = format!("{:?}", Dot::with_config(&graph, &[Config::EdgeNoLabel]));
+ assert_eq!(dot, "digraph {\n 0 [ label = \"\\\"A\\\"\" ]\n 1 [ label = \"\\\"B\\\"\" ]\n 0 -> 1 [ ]\n}\n");
+ }
+
+ #[test]
+ fn test_nodenolable_option() {
+ let graph = simple_graph();
+ let dot = format!("{:?}", Dot::with_config(&graph, &[Config::NodeNoLabel]));
+ assert_eq!(
+ dot,
+ "digraph {\n 0 [ ]\n 1 [ ]\n 0 -> 1 [ label = \"\\\"edge_label\\\"\" ]\n}\n"
+ );
+ }
+
+ #[test]
+ fn test_with_attr_getters() {
+ let graph = simple_graph();
+ let dot = format!(
+ "{:?}",
+ Dot::with_attr_getters(
+ &graph,
+ &[Config::NodeNoLabel, Config::EdgeNoLabel],
+ &|_, er| format!("label = \"{}\"", er.weight().to_uppercase()),
+ &|_, nr| format!("label = \"{}\"", nr.weight().to_lowercase()),
+ ),
+ );
+ assert_eq!(dot, "digraph {\n 0 [ label = \"a\"]\n 1 [ label = \"b\"]\n 0 -> 1 [ label = \"EDGE_LABEL\"]\n}\n");
+ }
+}