summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorseancarroll <seanc28@gmail.com>2020-12-11 00:43:12 -0600
committerseancarroll <seanc28@gmail.com>2020-12-11 00:43:12 -0600
commit6017a29f375a62f283db118d3fa36755cfa5ca72 (patch)
treeb9d3964d0ea5aee8f1c488f848343c18ac357cd1
parent487db23d14583455964e265e64a3a6b2b7633077 (diff)
downloaddotavious-6017a29f375a62f283db118d3fa36755cfa5ca72.zip
Fix lifetime and exclusive reference issues creating graphs with nodes
with attributes Initial design of having Node struct contain builder methods was causing lifetime and exclusive reference issues as builder methods needed to return &'a mut Self which I couldnt put into the graph nodes field of type Vec<Node<'a>>. To get around this I've included a NodeBuilder struct that has the specific builder methods along with a build method to create a Node. I'm still messing with an issue that doesnt allow chaining methods after new which is a real pita
-rw-r--r--src/lib.rs184
1 files changed, 175 insertions, 9 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 1e253f8..b29ce3e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -12,6 +12,7 @@ static INDENT: &str = " ";
/// Most of this comes from core rust. Where to provide attribution?
/// The text for a graphviz label on a node or edge.
+#[derive(Clone, PartialEq, Eq, Debug)]
pub enum AttributeText<'a> {
/// Preserves the text directly as is but wrapped in quotes.
@@ -334,6 +335,8 @@ pub struct Graph<'a, Ty = Directed> {
pub graph_attributes: Option<Vec<String>>,
+ // pub nodes: Vec<&'a Node<'a>>,
+
pub nodes: Vec<Node<'a>>,
pub edges: Vec<String>,
@@ -398,9 +401,23 @@ where Ty: GraphType
Ty::edge_slice()
}
- fn add_node(&mut self, id: &str) {
- self.nodes.push(Node::new(id.to_string()));
+ // fn add_node(&mut self, node: &'a Node<'a>) {
+ // self.nodes.push(node);
+ // }
+
+ fn add_node(&mut self, node: Node<'a>) {
+ self.nodes.push(node);
}
+
+ // fn add_node_mut(&mut self, node: &mut Node<'a>) {
+ // self.nodes.push(node);
+ // }
+
+ // fn create_node(&mut self, id: &str) -> Node {
+ // let node = Node::new(id.to_string());
+ // self.nodes.push(node);
+ // node
+ // }
}
// TODO: add node builder using "with" convention
@@ -437,7 +454,7 @@ impl<'a> Node<'a> {
}
/// Set the port for the node.
- pub fn port(&'a mut self, port: String) -> &'a mut Node {
+ pub fn port(mut self, port: String) -> Self {
self.port = Some(port);
self
}
@@ -447,7 +464,7 @@ impl<'a> Node<'a> {
// self
// }
- pub fn label(&'a mut self, text: String) -> &'a mut Node {
+ pub fn label(mut self, text: String) -> Self {
// self.label = Some(text);
self.attributes.insert("label".to_string(), QuottedStr(text.into()));
self
@@ -460,13 +477,13 @@ impl<'a> Node<'a> {
// }
/// Add an attribute to the node.
- pub fn attribute(&'a mut self, key: String, value: AttributeText<'a>) -> &'a mut Node {
+ pub fn attribute(mut self, key: String, value: AttributeText<'a>) -> Self {
self.attributes.insert(key, value);
self
}
/// Add multiple attribures to the node.
- pub fn attributes(&'a mut self, attributes: HashMap<String, AttributeText<'a>>) -> &'a mut Node {
+ pub fn attributes(mut self, attributes: HashMap<String, AttributeText<'a>>) -> Self {
self.attributes.extend(attributes);
self
}
@@ -478,7 +495,7 @@ impl<'a> Node<'a> {
if !self.attributes.is_empty() {
dot_string.push_str(" [");
for (key, value) in &self.attributes {
- dot_string.push_str(format!("{}=\"{}\"", key, value.to_dot_string()).as_str());
+ dot_string.push_str(format!("{}={}", key, value.to_dot_string()).as_str());
}
dot_string.push_str("]");
}
@@ -487,6 +504,72 @@ impl<'a> Node<'a> {
}
}
+
+pub struct NodeBuilder<'a> {
+ id: String,
+
+ port: Option<String>,
+
+ attributes: HashMap<String, AttributeText<'a>>,
+}
+
+impl<'a> NodeBuilder<'a> {
+ pub fn new(id: String) -> NodeBuilder<'a> {
+ NodeBuilder {
+ id: id,
+ port: None,
+ // compass: None,
+ // shape: None,
+ // label: None,
+ attributes: HashMap::new(),
+ }
+ }
+
+ /// Set the port for the node.
+ pub fn port<S: Into<String>>(&mut self, port: S) -> &mut Self {
+ self.port = Some(port.into());
+ self
+ }
+
+ // pub fn compass<'a>(&'a mut self, compass: Compass) -> &'a mut Node {
+ // self.compass = Some(compass);
+ // self
+ // }
+
+ pub fn label<S: Into<Cow<'a, str>>>(&mut self, text: S) -> &mut Self {
+ // self.label = Some(text);
+ self.attributes.insert("label".to_string(), QuottedStr(text.into()));
+ self
+ }
+
+ // TODO: create enum for shape at some point
+ // pub fn shape<'a>(&'a mut self, shape: String) -> &'a mut Node {
+ // self.attributes.insert("shape".to_string(), shape);
+ // self
+ // }
+
+ /// Add an attribute to the node.
+ pub fn attribute<S: Into<String>>(&mut self, key: S, value: AttributeText<'a>) -> &mut Self {
+ self.attributes.insert(key.into(), value);
+ self
+ }
+
+ /// Add multiple attribures to the node.
+ pub fn attributes(&'a mut self, attributes: HashMap<String, AttributeText<'a>>) -> &mut Self {
+ self.attributes.extend(attributes);
+ self
+ }
+
+ pub fn build(&self) -> Node<'a> {
+ Node {
+ // TODO: are these to_owned and clones necessary?
+ id: self.id.to_owned(),
+ port: self.port.to_owned(),
+ attributes: self.attributes.clone()
+ }
+ }
+}
+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum RenderOption {
NoEdgeLabels,
@@ -581,7 +664,7 @@ fn empty_graph() {
#[test]
fn single_node() {
let mut g = Graph::new(Some("single_node".to_string()));
- g.add_node("N0");
+ g.add_node(Node::new("N0".to_string()));
let r = test_input(g);
assert_eq!(
r.unwrap(),
@@ -590,4 +673,87 @@ fn single_node() {
}
"#
);
-} \ No newline at end of file
+}
+
+#[test]
+fn single_node_with_style() {
+ let mut g = Graph::new(Some("single_node".to_string()));
+ let mut nb = NodeBuilder::new("N0".to_string());
+ let node = nb.attribute("style", AttributeText::quotted("dashed")).build();
+
+ g.add_node(node);
+
+ let r = test_input(g);
+ assert_eq!(
+ r.unwrap(),
+ r#"digraph single_node {
+ N0 [style="dashed"];
+}
+"#
+ );
+}
+
+// #[test]
+// fn support_non_inline_builder() {
+// let mut g = Graph::new(Some("single_node".to_string()));
+
+// // TODO: having to split this is stupid. am i doing something wrong?
+// let mut node_builder = NodeBuilder::new("N0".to_string());
+// node_builder.attribute("style", AttributeText::quotted("dashed"));
+
+// if true {
+// node_builder.attribute("", AttributeText::quotted(""));
+// }
+
+// let node = node_builder.build();
+// g.add_node(node);
+
+// let r = test_input(g);
+// assert_eq!(
+// r.unwrap(),
+// r#"digraph single_node {
+// N0 [style="dashed"];
+// }
+// "#
+// );
+// }
+
+// #[test]
+// fn single_edge() {
+// let labels: Trivial = UnlabelledNodes(2);
+// let result = test_input(LabelledGraph::new(
+// "single_edge",
+// labels,
+// vec![edge(0, 1, "E", Style::None)],
+// None,
+// ));
+// assert_eq!(
+// result.unwrap(),
+// r#"digraph single_edge {
+// N0[label="N0"];
+// N1[label="N1"];
+// N0 -> N1[label="E"];
+// }
+// "#
+// );
+// }
+
+// #[test]
+// fn single_edge_with_style() {
+// let labels: Trivial = UnlabelledNodes(2);
+// let result = test_input(LabelledGraph::new(
+// "single_edge",
+// labels,
+// vec![edge(0, 1, "E", Style::Bold)],
+// None,
+// ));
+// assert_eq!(
+// result.unwrap(),
+// r#"digraph single_edge {
+// N0[label="N0"];
+// N1[label="N1"];
+// N0 -> N1[label="E"][style="bold"];
+// }
+// "#
+// );
+// } \ No newline at end of file