Add configurable functions for adding attributes to dotfile features
diff --git a/src/dot.rs b/src/dot.rs
index b9f44dc..be39a69 100644
--- a/src/dot.rs
+++ b/src/dot.rs
@@ -2,7 +2,7 @@
 
 use std::fmt::{self, Display, Write};
 
-use crate::visit::GraphRef;
+use crate::visit::{GraphBase, GraphRef, Data, GraphProp, NodeRef, EdgeRef, IntoEdgeReferences, IntoNodeReferences, NodeIndexable};
 
 /// `Dot` implements output to graphviz .dot format for a graph.
 ///
@@ -45,9 +45,14 @@
 ///
 /// // If you need multiple config options, just list them all in the slice.
 /// ```
-pub struct Dot<'a, G> {
+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"];
@@ -56,7 +61,7 @@
 
 impl<'a, G> Dot<'a, G>
 where
-    G: GraphRef,
+    G: GraphRef + IntoEdgeReferences + IntoNodeReferences,
 {
     /// Create a `Dot` formatting wrapper with default configuration.
     pub fn new(graph: G) -> Self {
@@ -65,7 +70,16 @@
 
     /// Create a `Dot` formatting wrapper with custom configuration.
     pub fn with_config(graph: G, config: &'a [Config]) -> Self {
-        Dot { graph, config }
+        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 }
     }
 }
 
@@ -80,16 +94,19 @@
     EdgeIndexLabel,
     /// Use no edge labels.
     EdgeNoLabel,
+    /// Use no node labels.
+    NodeNoLabel,
     /// Do not print the graph/digraph string.
     GraphContentOnly,
     #[doc(hidden)]
     _Incomplete(()),
 }
 
-use crate::visit::{Data, GraphProp, NodeRef};
-use crate::visit::{EdgeRef, IntoEdgeReferences, IntoNodeReferences, NodeIndexable};
 
-impl<'a, G> Dot<'a, G> {
+impl<'a, G> Dot<'a, G>
+where
+    G: GraphBase + IntoNodeReferences + IntoEdgeReferences,
+{
     fn graph_fmt<NF, EF, NW, EW>(
         &self,
         g: G,
@@ -99,7 +116,7 @@
     ) -> fmt::Result
     where
         G: NodeIndexable + IntoNodeReferences + IntoEdgeReferences,
-        G: GraphProp,
+        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,
@@ -110,34 +127,44 @@
 
         // output all labels
         for node in g.node_references() {
-            write!(f, "{}{}", INDENT, g.to_index(node.id()))?;
-            if self.config.contains(&Config::NodeIndexLabel) {
-                writeln!(f)?;
-            } else {
-                write!(f, " [label=\"")?;
-                node_fmt(node.weight(), &mut |d| Escaped(d).fmt(f))?;
-                writeln!(f, "\"]")?;
+            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())
+                g.to_index(edge.target()),
             )?;
-            if self.config.contains(&Config::EdgeNoLabel) {
-                writeln!(f)?;
-            } else if self.config.contains(&Config::EdgeIndexLabel) {
-                writeln!(f, " [label=\"{}\"]", i)?;
-            } else {
-                write!(f, " [label=\"")?;
-                edge_fmt(edge.weight(), &mut |d| Escaped(d).fmt(f))?;
-                writeln!(f, "\"]")?;
+            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) {
@@ -227,12 +254,72 @@
     }
 }
 
-#[test]
-fn test_escape() {
-    let mut buff = String::new();
-    {
-        let mut e = Escaper(&mut buff);
-        let _ = e.write_str("\" \\ \n");
+#[cfg(test)]
+mod test {
+    use crate::prelude::Graph;
+    use crate::visit::NodeRef;
+    use super::{Dot, Config, Escaper};
+    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");
     }
-    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");
+    }
 }
diff --git a/tests/graph.rs b/tests/graph.rs
index 6a1522a..e76c04f 100644
--- a/tests/graph.rs
+++ b/tests/graph.rs
@@ -1770,8 +1770,8 @@
         dot_output,
         // The single \ turns into four \\\\ because of Debug which turns it to \\ and then escaping each \ to \\.
         r#"digraph {
-    0 [label="Record { a: 1, b: \"abc\\\\\" }"]
-    0 -> 0 [label="(1, 2)"]
+    0 [ label = "Record { a: 1, b: \"abc\\\\\" }" ]
+    0 -> 0 [ label = "(1, 2)" ]
 }
 "#
     );
diff --git a/tests/stable_graph.rs b/tests/stable_graph.rs
index 2ca04ba..82cbcc7 100644
--- a/tests/stable_graph.rs
+++ b/tests/stable_graph.rs
@@ -303,10 +303,10 @@
     assert_eq!(
         dot_output,
         r#"digraph {
-    0 [label="x"]
-    1 [label="y"]
-    0 -> 0 [label="10"]
-    0 -> 1 [label="20"]
+    0 [ label = "x" ]
+    1 [ label = "y" ]
+    0 -> 0 [ label = "10" ]
+    0 -> 1 [ label = "20" ]
 }
 "#
     );