summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorseancarroll <seanc28@gmail.com>2020-12-14 21:19:30 -0600
committerseancarroll <seanc28@gmail.com>2020-12-14 21:19:30 -0600
commit007045bcb4b5541827b4b22f084ffa9313f8e4e8 (patch)
tree7da5b42bd6e654ebef367af3cbaa37459be224ef
parentd31be2dcc613dec53d7e6df6ae982834f5a46b15 (diff)
downloaddotavious-007045bcb4b5541827b4b22f084ffa9313f8e4e8.zip
working on graph attributes
-rw-r--r--src/lib.rs103
1 files changed, 97 insertions, 6 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 13968d9..27b058e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,9 +11,12 @@ use std::marker::PhantomData;
static INDENT: &str = " ";
// TODO: should we use a hashmap that retains insertion order?
-
// TODO: support adding edge based on index of nodes?
-
+// TODO: support fluent graph builder methods
+// TODO: handle render options
+// TODO: explicit attribute methods wit type safety and enforce constraints
+// i'm thinking we have NodeTraits/GraphTraints/EdgeTraits (what about none? is that a graph trait?)
+// which will have default methods that use an associated type field called "state" or "attribtues" etc
/// Most of this comes from core rust. Where to provide attribution?
/// The text for a graphviz label on a node or edge.
@@ -248,9 +251,68 @@ where Ty: GraphType
writeln!(w, "{}{} {} {{", strict, self.graph.as_slice(), id)?;
- // TODO: add global graph attributes
- for a in self.graph.attributes {
+ println!("graph has attributes: {}", !self.graph.attributes.is_empty());
+ // TODO: clean this up
+ for (key, value) in self.graph.attributes {
+ write!(w, "{}", INDENT);
+ match key {
+ AttributeType::Edge => {
+ write!(w, "edge");
+ if !value.is_empty() {
+ write!(w, " [");
+
+ let mut iter = value.iter();
+ let first = iter.next().unwrap();
+ write!(w, "{}={}", first.0, first.1.to_dot_string());
+ for (key, value) in iter {
+ write!(w, ", ");
+ write!(w, "{}={}", key, value.to_dot_string());
+ }
+ write!(w, "]");
+ }
+ writeln!(w, ";");
+ },
+ AttributeType::Graph => {
+ write!(w, "graph");
+ if !value.is_empty() {
+ write!(w, " [");
+
+ let mut iter = value.iter();
+ let first = iter.next().unwrap();
+ write!(w, "{}={}", first.0, first.1.to_dot_string());
+ for (key, value) in iter {
+ write!(w, ", ");
+ write!(w, "{}={}", key, value.to_dot_string());
+ }
+ write!(w, "]");
+ }
+ writeln!(w, ";");
+ },
+ AttributeType::Node => {
+ write!(w, "node");
+ if !value.is_empty() {
+ write!(w, " [");
+
+ let mut iter = value.iter();
+ let first = iter.next().unwrap();
+ write!(w, "{}={}", first.0, first.1.to_dot_string());
+ for (key, value) in iter {
+ write!(w, ", ");
+ write!(w, "{}={}", key, value.to_dot_string());
+ }
+ write!(w, "]");
+ }
+ writeln!(w, ";");
+ },
+ AttributeType::None => {
+ if !value.is_empty() {
+ for (key, value) in value.iter() {
+ writeln!(w, "{}={};", key, value.to_dot_string());
+ }
+ }
+ }
+ }
}
for n in self.graph.nodes {
@@ -303,7 +365,8 @@ pub enum Config {
_Incomplete(()),
}
-enum AttributeType {
+#[derive(Hash, Eq, PartialEq, Debug, Clone)]
+pub enum AttributeType {
Graph,
Node,
Edge,
@@ -333,7 +396,7 @@ pub struct Graph<'a, Ty = Directed> {
// TODO: support multiple comments
pub comment: Option<String>,
- pub attributes: HashMap<String, AttributeText<'a>>,
+ pub attributes: HashMap<AttributeType, HashMap<String, AttributeText<'a>>>,
pub nodes: Vec<Node<'a>>,
@@ -406,6 +469,12 @@ where Ty: GraphType
pub fn add_edge(&mut self, edge: Edge<'a>) {
self.edges.push(edge);
}
+
+ pub fn attribute(&mut self, attribute_type: AttributeType, key: String, value: AttributeText<'a>) {
+ println!("attribute type: {:?}", attribute_type);
+ self.attributes.entry(attribute_type).or_insert(HashMap::new())
+ .insert(key, value);
+ }
}
// TODO: add node builder using "with" convention
@@ -955,4 +1024,26 @@ fn single_edge_with_style() {
}
"#
);
+}
+
+#[test]
+fn graph_attributes() {
+ let mut g = Graph::new(Some("graph_attributes".to_string()));
+ g.attribute(AttributeType::None, "ranksep".to_string(), AttributeText::attr("0.5"));
+ g.attribute(AttributeType::Graph, "rankdir".to_string(), AttributeText::attr("LR"));
+ g.attribute(AttributeType::Edge, "minlen".to_string(), AttributeText::attr("1"));
+ g.attribute(AttributeType::Node, "style".to_string(), AttributeText::attr("filled"));
+
+ let r = test_input(g);
+
+ assert_eq!(
+ r.unwrap(),
+ r#"digraph graph_attributes {
+ edge [minlen=1];
+ graph [rankdir=LR];
+ node [style=filled];
+ ranksep=0.5;
+}
+"#
+ );
} \ No newline at end of file