From f0aea2cce064736cf1903ee4c3b54bbb7c5c4dbe Mon Sep 17 00:00:00 2001 From: Ian Akotey Date: Sat, 1 Oct 2022 19:34:56 +0000 Subject: [PATCH 01/10] added url and label_url to edges --- src/edge.rs | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/edge.rs b/src/edge.rs index 814fa3d..7d602d3 100644 --- a/src/edge.rs +++ b/src/edge.rs @@ -10,6 +10,8 @@ pub struct Edge { from: String, to: String, label: String, + label_url: String, + url: String, style: Style, start_arrow: Arrow, end_arrow: Arrow, @@ -18,7 +20,13 @@ pub struct Edge { impl Edge { pub fn new(from: &str, to: &str, label: &str) -> Self { - Edge { from: String::from(from), to: String::from(to), label: String::from(label), style: Style::None, start_arrow: Arrow::default(), end_arrow: Arrow::default(), color: None } + Edge { + from: String::from(from), to: String::from(to), + label: String::from(label), label_url: Default::default(), + color: None, style: Style::None, + start_arrow: Arrow::default(), end_arrow: Arrow::default(), + url: Default::default() + } } pub fn label(&mut self, label: &str) -> Self { @@ -54,19 +62,45 @@ impl Edge { edge } + pub fn label_url(&mut self, url: String) -> Self { + let mut edge = self.clone(); + edge.label_url = url; + edge + } + + pub fn url(&mut self, url: String) -> Self { + let mut edge = self.clone(); + edge.url = url; + edge + } + pub fn to_dot_string(&self, edge_symbol: &str) -> String { let colorstring: String; let escaped_label: &String = "e_string(self.label.clone()); let start_arrow_s: String = self.start_arrow.to_dot_string(); let end_arrow_s: String = self.end_arrow.to_dot_string(); - + let escaped_label_url: &String = "e_string(self.label_url.clone()); + let escaped_url: &String = "e_string(self.url.clone()); + let mut text = vec!["\"", self.from.as_str(), "\" ", - edge_symbol, " ", - "\"", self.to.as_str(), "\"",]; - + edge_symbol, " ", + "\"", self.to.as_str(), "\"",]; + text.push("[label="); text.push(escaped_label.as_str()); text.push("]"); + + if !self.label_url.is_empty(){ + text.push("[labelURL="); + text.push(escaped_label_url.as_str()); + text.push("]"); + } + + if !self.url.is_empty(){ + text.push("[URL="); + text.push(escaped_url.as_str()); + text.push("]"); + } if self.style != Style::None { text.push("[style=\""); From c367dea7f9317d215ca4a6cc1137d30e46c37281 Mon Sep 17 00:00:00 2001 From: Ian Akotey Date: Sat, 1 Oct 2022 19:45:26 +0000 Subject: [PATCH 02/10] added tests for edge urls --- tests/integration.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/integration.rs b/tests/integration.rs index 10632e4..45cd43e 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -420,4 +420,52 @@ r#"digraph G { let result = graph.to_dot_string(); result.unwrap(); } + + #[test] + fn single_edge_with_url() { + let mut graph = Graph::new("single_edge", Kind::Digraph); + graph.add_node(Node::new("N0")); + graph.add_node(Node::new("N1")); + graph.add_edge(Edge::new("N0", "N1", "E").url("https://example.com/".into())); + assert_eq!(graph.to_dot_string().unwrap(), +r#"digraph single_edge { + "N0"[label="N0"]; + "N1"[label="N1"]; + "N0" -> "N1"[label="E"][URL="https://example.com/"]; +} +"#); + } + + #[test] + fn single_edge_with_label_url() { + let mut graph = Graph::new("single_edge", Kind::Digraph); + graph.add_node(Node::new("N0")); + graph.add_node(Node::new("N1")); + graph.add_edge(Edge::new("N0", "N1", "E").label_url("https://example.com/".into())); + assert_eq!(graph.to_dot_string().unwrap(), +r#"digraph single_edge { + "N0"[label="N0"]; + "N1"[label="N1"]; + "N0" -> "N1"[label="E"][labelURL="https://example.com/"]; +} +"#); + } + + #[test] + fn single_edge_with_label_url_and_url() { + let mut graph = Graph::new("single_edge", Kind::Digraph); + graph.add_node(Node::new("N0")); + graph.add_node(Node::new("N1")); + graph.add_edge(Edge::new("N0", "N1", "E") + .url("https://example.com/".into()) + .label_url("https://example.com/".into()) + ); + assert_eq!(graph.to_dot_string().unwrap(), +r#"digraph single_edge { + "N0"[label="N0"]; + "N1"[label="N1"]; + "N0" -> "N1"[label="E"][labelURL="https://example.com/"][URL="https://example.com/"]; +} +"#); + } } \ No newline at end of file From 97fe2bc2ebf01722d599e949e9463d096fa5850c Mon Sep 17 00:00:00 2001 From: Ian Akotey Date: Sat, 1 Oct 2022 20:23:50 +0000 Subject: [PATCH 03/10] added .vscode to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index fc80547..75e8162 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ Cargo.lock # .dot file for tests **/*.dot + +# Editor related configuratigurations +.vscode \ No newline at end of file From 44f83223db3193f5f815b01e908392a696ddbe98 Mon Sep 17 00:00:00 2001 From: Ian Akotey Date: Sat, 1 Oct 2022 20:48:12 +0000 Subject: [PATCH 04/10] Added url attribute to graph and subgraph --- src/graph.rs | 24 +++++++++++++++++------- src/subgraph.rs | 17 ++++++++++++++++- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index feaee1f..e231855 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -1,14 +1,16 @@ use crate::{ node::{Node}, - edge::{Edge}, subgraph::Subgraph, + edge::{Edge}, subgraph::Subgraph, utils::quote_string, }; use std::io::prelude::*; use std::io; /// Entry point of this library, use `to_dot_string` to get the string output. +#[derive(Clone)] pub struct Graph { name: String, kind: Kind, + url: String, nodes: Vec, edges: Vec, subgraph: Vec @@ -16,7 +18,7 @@ pub struct Graph { impl Graph { pub fn new(name: &str, kind: Kind) -> Graph { - Graph { name: String::from(name), kind: kind, nodes: vec![], edges: vec![], subgraph: vec![] } + Graph { name: String::from(name), kind: kind, nodes: vec![], edges: vec![], subgraph: vec![], url: Default::default() } } pub fn add_node(&mut self, node: Node) -> () { @@ -31,6 +33,12 @@ impl Graph { self.subgraph.push(subgraph.edgeop(self.kind.edgeop())) } + pub fn url(&mut self, url: String) -> Self { + let mut edge = self.clone(); + edge.url = url; + edge + } + pub fn to_dot_string(&self) -> io::Result { let mut writer = Vec::new(); self.render_opts(&mut writer).unwrap(); @@ -41,11 +49,7 @@ impl Graph { /// Renders graph `g` into the writer `w` in DOT syntax. /// (Main entry point for the library.) - fn render_opts<'a, - W: Write> - (&self, - w: &mut W) - -> io::Result<()> { + fn render_opts<'a,W: Write>(&self, w: &mut W) -> io::Result<()> { fn writeln(w: &mut W, arg: &[&str]) -> io::Result<()> { for &s in arg { w.write_all(s.as_bytes())?; @@ -58,6 +62,12 @@ impl Graph { } writeln(w, &[self.kind.keyword(), " ", self.name.as_str(), " {"])?; + + if !self.url.is_empty(){ + indent(w)?; + writeln(w, &["URL=", quote_string(self.url.clone()).as_str()])?; + } + for n in self.subgraph.iter() { indent(w)?; let mut text: Vec<&str> = vec![]; diff --git a/src/subgraph.rs b/src/subgraph.rs index fdff9c6..886d18c 100644 --- a/src/subgraph.rs +++ b/src/subgraph.rs @@ -15,11 +15,12 @@ pub struct Subgraph { style: Style, color: Option, edgeop: String, + url: String } impl Subgraph { pub fn new(name: &str) -> Self { - Subgraph { name: new_name(name), nodes: vec![], edges: vec![], label: String::new(), style: Style::None, color: None, edgeop: String::from(Kind::Digraph.edgeop())} + Subgraph { name: new_name(name), nodes: vec![], edges: vec![], label: String::new(), style: Style::None, color: None, edgeop: String::from(Kind::Digraph.edgeop()), url: Default::default() } } pub fn add_node(&mut self, node: Node) -> () { @@ -61,9 +62,23 @@ impl Subgraph { subg } + pub fn url(&mut self, url: String) -> Self { + let mut edge = self.clone(); + edge.url = url; + edge + } + pub fn to_dot_string(&self) -> String { let mut text = vec!["subgraph ", self.name.as_str(), " {\n "]; + let escaped_url: String; + if !self.url.is_empty(){ + escaped_url = quote_string(self.url.clone()); + text.push("URL="); + text.push(escaped_url.as_str()); + text.push(";\n "); + } + text.push("label=\""); text.push(self.label.as_str()); text.push("\";\n "); From 32c1c5ed8089ca3abf93216380b72a5bcdd680e3 Mon Sep 17 00:00:00 2001 From: Ian Akotey Date: Sat, 1 Oct 2022 20:48:34 +0000 Subject: [PATCH 05/10] Added tests for url attribute in graph and subgraph --- tests/integration.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/integration.rs b/tests/integration.rs index 45cd43e..9442679 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -28,6 +28,19 @@ r#"digraph single_node { "#); } + #[test] + fn single_node_with_url() { + let mut graph = Graph::new("single_node", Kind::Digraph).url("https://example.com/".into()); + let node = Node::new("N0"); + graph.add_node(node); + assert_eq!(graph.to_dot_string().unwrap(), +r#"digraph single_node { + URL="https://example.com/" + "N0"[label="N0"]; +} +"#); + } + #[test] fn dot_in_node_name() { let mut graph = Graph::new("single_node", Kind::Digraph); @@ -249,7 +262,7 @@ r#"graph g { let mut c1 = Subgraph::new("cluster_0").label(""); c1.add_node(Node::new("N0")); c1.add_node(Node::new("N1")); - let mut c2 = Subgraph::new("cluster_1").label(""); + let mut c2 = Subgraph::new("cluster_1").url("https://example.com/".into()).label(""); c2.add_node(Node::new("N2")); c2.add_node(Node::new("N3")); graph.add_subgraph(c1); @@ -268,6 +281,7 @@ r#"digraph di { "N1"[label="N1"]; } subgraph cluster_1 { + URL="https://example.com/"; label=""; "N2"[label="N2"]; "N3"[label="N3"]; From 33562edf36c7eecace2c81a9339994e2c7918926 Mon Sep 17 00:00:00 2001 From: Ian Akotey Date: Sat, 1 Oct 2022 21:32:20 +0000 Subject: [PATCH 06/10] minor refactor to url method --- src/graph.rs | 6 +++--- src/subgraph.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/graph.rs b/src/graph.rs index e231855..a7a1853 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -34,9 +34,9 @@ impl Graph { } pub fn url(&mut self, url: String) -> Self { - let mut edge = self.clone(); - edge.url = url; - edge + let mut graph = self.clone(); + graph.url = url; + graph } pub fn to_dot_string(&self) -> io::Result { diff --git a/src/subgraph.rs b/src/subgraph.rs index 886d18c..7d0a80c 100644 --- a/src/subgraph.rs +++ b/src/subgraph.rs @@ -63,9 +63,9 @@ impl Subgraph { } pub fn url(&mut self, url: String) -> Self { - let mut edge = self.clone(); - edge.url = url; - edge + let mut sub_graph = self.clone(); + sub_graph.url = url; + sub_graph } pub fn to_dot_string(&self) -> String { From f50a78015b498478f4f056adf2a40638d6b87af7 Mon Sep 17 00:00:00 2001 From: Ian Akotey Date: Sat, 1 Oct 2022 21:34:25 +0000 Subject: [PATCH 07/10] added url to nodes --- src/node.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/node.rs b/src/node.rs index efbc6f6..b9a3b36 100644 --- a/src/node.rs +++ b/src/node.rs @@ -13,12 +13,13 @@ pub struct Node { label: String, style: Style, color: Option, - shape: Option + shape: Option, + url: String } impl Node { pub fn new(name: &str) -> Self { - Node { name: new_name(name), label: String::from(name), style: Style::None, color: None, shape: None } + Node { name: new_name(name), label: String::from(name), style: Style::None, color: None, shape: None, url: Default::default() } } pub fn label(&self, label: &str) -> Self { @@ -51,18 +52,31 @@ impl Node { node } + pub fn url(&mut self, url: String) -> Self { + let mut node = self.clone(); + node.url = url; + node + } + pub fn to_dot_string(&self) -> String { let colorstring: String; - let escaped: String = quote_string(self.label.clone()); + let escaped_label: String = quote_string(self.label.clone()); + let escaped_url: String = quote_string(self.url.clone()); let shape: String; let mut text = vec!["\"", self.name.as_str(), "\""]; text.push("[label="); - text.push(escaped.as_str()); + text.push(escaped_label.as_str()); text.push("]"); + if !self.url.is_empty() { + text.push("[URL="); + text.push(escaped_url.as_str()); + text.push("]"); + } + if self.style != Style::None { text.push("[style=\""); text.push(self.style.as_slice()); From ed91ad9d7a645eee2fb729b00275af98a4a12dab Mon Sep 17 00:00:00 2001 From: Ian Akotey Date: Sat, 1 Oct 2022 21:34:35 +0000 Subject: [PATCH 08/10] added test for node url --- tests/integration.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration.rs b/tests/integration.rs index 9442679..90fcc04 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -31,12 +31,12 @@ r#"digraph single_node { #[test] fn single_node_with_url() { let mut graph = Graph::new("single_node", Kind::Digraph).url("https://example.com/".into()); - let node = Node::new("N0"); + let node = Node::new("N0").url("https://example.com/".into()); graph.add_node(node); assert_eq!(graph.to_dot_string().unwrap(), r#"digraph single_node { URL="https://example.com/" - "N0"[label="N0"]; + "N0"[label="N0"][URL="https://example.com/"]; } "#); } From c6251921f8141fc5ed397fd7376b6002613264c5 Mon Sep 17 00:00:00 2001 From: Ian Akotey Date: Sat, 1 Oct 2022 22:15:02 +0000 Subject: [PATCH 09/10] formatted with rustfmt --- src/arrow.rs | 63 +++++++++++++++++-------------------- src/edge.rs | 83 ++++++++++++++++++++++++++++--------------------- src/graph.rs | 26 +++++++++------- src/lib.rs | 57 +++++++++++++++++---------------- src/node.rs | 26 +++++++++------- src/style.rs | 2 +- src/subgraph.rs | 37 ++++++++++++---------- src/utils.rs | 2 +- 8 files changed, 156 insertions(+), 140 deletions(-) diff --git a/src/arrow.rs b/src/arrow.rs index 9663fa1..14e9a85 100644 --- a/src/arrow.rs +++ b/src/arrow.rs @@ -1,4 +1,3 @@ - /// This structure holds all information that can describe an arrow connected to /// either start or end of an edge. #[derive(Clone, Hash, PartialEq, Eq)] @@ -16,9 +15,7 @@ impl Arrow { /// Arrow constructor which returns a default arrow pub fn default() -> Arrow { - Arrow { - arrows: vec![], - } + Arrow { arrows: vec![] } } /// Arrow constructor which returns an empty arrow @@ -31,7 +28,7 @@ impl Arrow { /// Arrow constructor which returns a regular triangle arrow, without modifiers pub fn normal() -> Arrow { Arrow { - arrows: vec![ArrowShape::normal()] + arrows: vec![ArrowShape::normal()], } } @@ -47,12 +44,11 @@ impl Arrow { let mut cow = String::new(); for arrow in &self.arrows { cow.push_str(&arrow.to_dot_string()); - }; + } cow } } - impl Into for [ArrowShape; 2] { fn into(self) -> Arrow { Arrow { @@ -103,14 +99,13 @@ pub enum Side { impl Side { pub fn as_slice(self) -> &'static str { match self { - Side::Left => "l", + Side::Left => "l", Side::Right => "r", - Side::Both => "", + Side::Both => "", } } } - /// This enumeration represents all possible arrow edge /// as defined in [grapviz documentation](http://www.graphviz.org/content/arrow-shapes). #[derive(Clone, Copy, Hash, PartialEq, Eq)] @@ -199,37 +194,37 @@ impl ArrowShape { pub fn to_dot_string(&self) -> String { let mut res = String::new(); match *self { - Box(fill, side) | ICurve(fill, side)| Diamond(fill, side) | - Inv(fill, side) | Normal(fill, side)=> { + Box(fill, side) + | ICurve(fill, side) + | Diamond(fill, side) + | Inv(fill, side) + | Normal(fill, side) => { res.push_str(fill.as_slice()); match side { Side::Left | Side::Right => res.push_str(side.as_slice()), - Side::Both => {}, + Side::Both => {} }; - }, - Dot(fill) => res.push_str(fill.as_slice()), - Crow(side) | Curve(side) | Tee(side) - | Vee(side) => { - match side { - Side::Left | Side::Right => res.push_str(side.as_slice()), - Side::Both => {}, - } } - NoArrow => {}, + Dot(fill) => res.push_str(fill.as_slice()), + Crow(side) | Curve(side) | Tee(side) | Vee(side) => match side { + Side::Left | Side::Right => res.push_str(side.as_slice()), + Side::Both => {} + }, + NoArrow => {} }; match *self { - NoArrow => res.push_str("none"), - Normal(_, _) => res.push_str("normal"), - Box(_, _) => res.push_str("box"), - Crow(_) => res.push_str("crow"), - Curve(_) => res.push_str("curve"), - ICurve(_, _) => res.push_str("icurve"), - Diamond(_, _) => res.push_str("diamond"), - Dot(_) => res.push_str("dot"), - Inv(_, _) => res.push_str("inv"), - Tee(_) => res.push_str("tee"), - Vee(_) => res.push_str("vee"), + NoArrow => res.push_str("none"), + Normal(_, _) => res.push_str("normal"), + Box(_, _) => res.push_str("box"), + Crow(_) => res.push_str("crow"), + Curve(_) => res.push_str("curve"), + ICurve(_, _) => res.push_str("icurve"), + Diamond(_, _) => res.push_str("diamond"), + Dot(_) => res.push_str("dot"), + Inv(_, _) => res.push_str("inv"), + Tee(_) => res.push_str("tee"), + Vee(_) => res.push_str("vee"), }; res } -} \ No newline at end of file +} diff --git a/src/edge.rs b/src/edge.rs index 7d602d3..b9625bb 100644 --- a/src/edge.rs +++ b/src/edge.rs @@ -1,8 +1,4 @@ -use crate::{ - arrow::{Arrow}, - style::{Style}, - utils::{quote_string}, -}; +use crate::{arrow::Arrow, style::Style, utils::quote_string}; /// `Graph`'s edge. #[derive(Clone)] @@ -20,12 +16,16 @@ pub struct Edge { impl Edge { pub fn new(from: &str, to: &str, label: &str) -> Self { - Edge { - from: String::from(from), to: String::from(to), - label: String::from(label), label_url: Default::default(), - color: None, style: Style::None, - start_arrow: Arrow::default(), end_arrow: Arrow::default(), - url: Default::default() + Edge { + from: String::from(from), + to: String::from(to), + label: String::from(label), + label_url: Default::default(), + color: None, + style: Style::None, + start_arrow: Arrow::default(), + end_arrow: Arrow::default(), + url: Default::default(), } } @@ -45,7 +45,7 @@ impl Edge { let mut edge = self.clone(); edge.color = match color { Some(c) => Some(String::from(c)), - None => None + None => None, }; edge } @@ -81,26 +81,33 @@ impl Edge { let end_arrow_s: String = self.end_arrow.to_dot_string(); let escaped_label_url: &String = "e_string(self.label_url.clone()); let escaped_url: &String = "e_string(self.url.clone()); - - let mut text = vec!["\"", self.from.as_str(), "\" ", - edge_symbol, " ", - "\"", self.to.as_str(), "\"",]; - + + let mut text = vec![ + "\"", + self.from.as_str(), + "\" ", + edge_symbol, + " ", + "\"", + self.to.as_str(), + "\"", + ]; + text.push("[label="); text.push(escaped_label.as_str()); text.push("]"); - - if !self.label_url.is_empty(){ - text.push("[labelURL="); - text.push(escaped_label_url.as_str()); - text.push("]"); - } - - if !self.url.is_empty(){ - text.push("[URL="); - text.push(escaped_url.as_str()); - text.push("]"); - } + + if !self.label_url.is_empty() { + text.push("[labelURL="); + text.push(escaped_label_url.as_str()); + text.push("]"); + } + + if !self.url.is_empty() { + text.push("[URL="); + text.push(escaped_url.as_str()); + text.push("]"); + } if self.style != Style::None { text.push("[style=\""); @@ -109,9 +116,7 @@ impl Edge { } let color: Option = match &self.color { - Some(l) => { - Some((*l).clone()) - }, + Some(l) => Some((*l).clone()), None => None, }; if let Some(c) = color { @@ -125,10 +130,18 @@ impl Edge { let mut arrow_str: String = String::new(); if !self.start_arrow.is_default() || !self.end_arrow.is_default() { if !self.end_arrow.is_default() { - arrow_text.push(vec!["arrowhead=\"", &end_arrow_s, "\""].into_iter().collect()); + arrow_text.push( + vec!["arrowhead=\"", &end_arrow_s, "\""] + .into_iter() + .collect(), + ); } if !self.start_arrow.is_default() { - arrow_text.push(vec!["arrowtail=\"", &start_arrow_s, "\""].into_iter().collect()); + arrow_text.push( + vec!["arrowtail=\"", &start_arrow_s, "\""] + .into_iter() + .collect(), + ); } if !self.start_arrow.is_default() && !self.end_arrow.is_default() { arrow_text.push(String::from("dir=\"both\"")); @@ -144,4 +157,4 @@ impl Edge { text.push(";"); return text.into_iter().collect(); } -} \ No newline at end of file +} diff --git a/src/graph.rs b/src/graph.rs index a7a1853..8a57259 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -1,9 +1,6 @@ -use crate::{ - node::{Node}, - edge::{Edge}, subgraph::Subgraph, utils::quote_string, -}; -use std::io::prelude::*; +use crate::{edge::Edge, node::Node, subgraph::Subgraph, utils::quote_string}; use std::io; +use std::io::prelude::*; /// Entry point of this library, use `to_dot_string` to get the string output. #[derive(Clone)] @@ -13,12 +10,19 @@ pub struct Graph { url: String, nodes: Vec, edges: Vec, - subgraph: Vec + subgraph: Vec, } impl Graph { pub fn new(name: &str, kind: Kind) -> Graph { - Graph { name: String::from(name), kind: kind, nodes: vec![], edges: vec![], subgraph: vec![], url: Default::default() } + Graph { + name: String::from(name), + kind: kind, + nodes: vec![], + edges: vec![], + subgraph: vec![], + url: Default::default(), + } } pub fn add_node(&mut self, node: Node) -> () { @@ -49,7 +53,7 @@ impl Graph { /// Renders graph `g` into the writer `w` in DOT syntax. /// (Main entry point for the library.) - fn render_opts<'a,W: Write>(&self, w: &mut W) -> io::Result<()> { + fn render_opts<'a, W: Write>(&self, w: &mut W) -> io::Result<()> { fn writeln(w: &mut W, arg: &[&str]) -> io::Result<()> { for &s in arg { w.write_all(s.as_bytes())?; @@ -63,7 +67,7 @@ impl Graph { writeln(w, &[self.kind.keyword(), " ", self.name.as_str(), " {"])?; - if !self.url.is_empty(){ + if !self.url.is_empty() { indent(w)?; writeln(w, &["URL=", quote_string(self.url.clone()).as_str()])?; } @@ -111,7 +115,7 @@ impl Kind { pub fn keyword(&self) -> &'static str { match *self { Kind::Digraph => "digraph", - Kind::Graph => "graph" + Kind::Graph => "graph", } } @@ -122,4 +126,4 @@ impl Kind { Kind::Graph => "--", } } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 0124db8..a8aabdc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,18 +5,18 @@ // except according to those terms. //! A library for generating Graphviz DOT language files. -//! +//! //! The very basic three parts of a DOT file and in this library is `Graph`, //! `Node` and `Edge`. `Graph` is the entry point of the library. You could //! generate an empty graph .dot file by simply use: -//! +//! //! ```rust //! use dot_graph::{Graph, Kind}; -//! +//! //! let graph = Graph::new("empty_graph", Kind::Digraph); -//! +//! //! let dot_string = graph.to_dot_string().unwrap(); -//! +//! //! assert_eq!(dot_string, //! r#"digraph empty_graph { //! } @@ -24,18 +24,18 @@ //! ``` //! //! In order to add some basic nodes and edges: -//! +//! //! ```rust //! use dot_graph::{Graph, Kind, Node, Edge}; -//! +//! //! let mut graph = Graph::new("single_edge", Kind::Digraph); -//! +//! //! graph.add_node(Node::new("N0")); //! graph.add_node(Node::new("N1")); //! graph.add_edge(Edge::new("N0", "N1", "E")); -//! +//! //! let dot_string = graph.to_dot_string().unwrap(); -//! +//! //! assert_eq!(dot_string, //! r#"digraph single_edge { //! "N0"[label="N0"]; @@ -44,19 +44,19 @@ //! } //! "#); //! ``` -//! +//! //! If you want add some more attributes, like style, arrow, color, //! you could call these methods in a chain, like: -//! +//! //! ```rust //! use dot_graph::{Graph, Kind, Node, Edge, Style}; -//! +//! //! let mut graph = Graph::new("single_edge", Kind::Digraph); -//! +//! //! graph.add_node(Node::new("N0")); //! graph.add_node(Node::new("N1")); //! graph.add_edge(Edge::new("N0", "N1", "E").style(Style::Bold).color(Some("red"))); -//! +//! //! assert_eq!(graph.to_dot_string().unwrap(), //! r#"digraph single_edge { //! "N0"[label="N0"]; @@ -65,9 +65,9 @@ //! } //! "#); //! ``` -//! +//! //! After version 0.2.1, dot_graph support subgraph generation, for example: -//! +//! //! ```rust //! #[test] //! fn test_subgraph() { @@ -85,7 +85,7 @@ //! graph.add_edge(Edge::new("N1", "N3", "")); //! graph.add_edge(Edge::new("N2", "N3", "")); //! -//! +//! //! assert_eq!(graph.to_dot_string().unwrap(), //! r#"digraph di { //! subgraph cluster_0 { @@ -106,30 +106,29 @@ //! "#); //! } //! ``` -//! +//! //! For more examples, please check the tests. -//! +//! //! The library is under active development, we'll include more dot attributes //! in the future. -//! +//! //! # References //! //! * [Graphviz](http://graphviz.org/) //! //! * [DOT language](http://graphviz.org/doc/info/lang.html) -mod style; mod arrow; -mod node; mod edge; mod graph; -mod utils; +mod node; +mod style; mod subgraph; +mod utils; -pub use style::Style; -pub use arrow::{Arrow, ArrowShape, Side, Fill}; -pub use node::{Node}; -pub use edge::{Edge}; +pub use arrow::{Arrow, ArrowShape, Fill, Side}; +pub use edge::Edge; pub use graph::{Graph, Kind}; +pub use node::Node; +pub use style::Style; pub use subgraph::Subgraph; - diff --git a/src/node.rs b/src/node.rs index b9a3b36..862074a 100644 --- a/src/node.rs +++ b/src/node.rs @@ -1,10 +1,6 @@ /// each node is an index in a vector in the graph. // pub type Node = usize; - -use crate::{ - style::Style, - utils::{quote_string}, -}; +use crate::{style::Style, utils::quote_string}; /// `Graph`'s node #[derive(Clone)] @@ -14,12 +10,19 @@ pub struct Node { style: Style, color: Option, shape: Option, - url: String + url: String, } impl Node { pub fn new(name: &str) -> Self { - Node { name: new_name(name), label: String::from(name), style: Style::None, color: None, shape: None, url: Default::default() } + Node { + name: new_name(name), + label: String::from(name), + style: Style::None, + color: None, + shape: None, + url: Default::default(), + } } pub fn label(&self, label: &str) -> Self { @@ -38,7 +41,7 @@ impl Node { let mut node = self.clone(); match shape { Some(s) => node.shape = Some(String::from(s)), - None => node.shape = None + None => node.shape = None, } node } @@ -47,7 +50,7 @@ impl Node { let mut node = self.clone(); node.color = match color { Some(c) => Some(String::from(c)), - None => None + None => None, }; node } @@ -100,7 +103,6 @@ impl Node { text.push(";"); return text.into_iter().collect(); } - } /// Check if the node's name is illegal. @@ -126,7 +128,7 @@ fn new_name(name: &str) -> String { if !chars.all(is_constituent) { panic!("The name of the node should only contain letter/number/underscore/dot") } - return String::from(name); + return String::from(name); fn is_letter_or_underscore_or_dot(c: char) -> bool { in_range('a', c, 'z') || in_range('A', c, 'Z') || c == '_' || c == '.' @@ -137,4 +139,4 @@ fn new_name(name: &str) -> String { fn in_range(low: char, c: char, high: char) -> bool { low as usize <= c as usize && c as usize <= high as usize } -} \ No newline at end of file +} diff --git a/src/style.rs b/src/style.rs index eeb36a6..0694601 100644 --- a/src/style.rs +++ b/src/style.rs @@ -32,4 +32,4 @@ impl Style { Style::Wedged => "wedged", } } -} \ No newline at end of file +} diff --git a/src/subgraph.rs b/src/subgraph.rs index 7d0a80c..f819f35 100644 --- a/src/subgraph.rs +++ b/src/subgraph.rs @@ -1,9 +1,4 @@ -use crate::{ - node::Node, - style::Style, - utils::quote_string, Edge, - Kind -}; +use crate::{node::Node, style::Style, utils::quote_string, Edge, Kind}; /// `Graph`'s subgraph #[derive(Clone)] @@ -15,12 +10,21 @@ pub struct Subgraph { style: Style, color: Option, edgeop: String, - url: String + url: String, } impl Subgraph { pub fn new(name: &str) -> Self { - Subgraph { name: new_name(name), nodes: vec![], edges: vec![], label: String::new(), style: Style::None, color: None, edgeop: String::from(Kind::Digraph.edgeop()), url: Default::default() } + Subgraph { + name: new_name(name), + nodes: vec![], + edges: vec![], + label: String::new(), + style: Style::None, + color: None, + edgeop: String::from(Kind::Digraph.edgeop()), + url: Default::default(), + } } pub fn add_node(&mut self, node: Node) -> () { @@ -51,7 +55,7 @@ impl Subgraph { let mut subg = self.clone(); subg.color = match color { Some(c) => Some(String::from(c)), - None => None + None => None, }; subg } @@ -72,7 +76,7 @@ impl Subgraph { let mut text = vec!["subgraph ", self.name.as_str(), " {\n "]; let escaped_url: String; - if !self.url.is_empty(){ + if !self.url.is_empty() { escaped_url = quote_string(self.url.clone()); text.push("URL="); text.push(escaped_url.as_str()); @@ -97,7 +101,8 @@ impl Subgraph { text.push(";\n "); } - let subgraph_node_names = self.nodes + let subgraph_node_names = self + .nodes .iter() .map(|n| n.to_dot_string()) .collect::>() @@ -108,10 +113,10 @@ impl Subgraph { text.push(&subgraph_node_names); text.push("\n "); } - let edge_symbol = &self.edgeop; - let subgraph_edge_strs = self.edges + let subgraph_edge_strs = self + .edges .iter() .map(|e| e.to_dot_string(&edge_symbol)) .collect::>() @@ -124,9 +129,7 @@ impl Subgraph { } text.push("}"); - - return text.into_iter().collect(); } } @@ -158,7 +161,7 @@ fn new_name(name: &str) -> String { if !name.starts_with("cluster_") { panic!("The name of the subgraph should start with \"cluster_\"") } - return String::from(name); + return String::from(name); fn is_letter_or_underscore(c: char) -> bool { in_range('a', c, 'z') || in_range('A', c, 'Z') || c == '_' @@ -169,4 +172,4 @@ fn new_name(name: &str) -> String { fn in_range(low: char, c: char, high: char) -> bool { low as usize <= c as usize && c as usize <= high as usize } -} \ No newline at end of file +} diff --git a/src/utils.rs b/src/utils.rs index b2c5056..6092f6a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,3 @@ pub fn quote_string(s: String) -> String { format!("\"{}\"", s) -} \ No newline at end of file +} From 372e1965ce4b0800251dde12bbdbd85749e5a79e Mon Sep 17 00:00:00 2001 From: Ian Akotey Date: Thu, 6 Oct 2022 13:01:32 +0000 Subject: [PATCH 10/10] Removed unecessary cloning in methods that return Self --- src/edge.rs | 49 +++++++++++++++++++++---------------------------- src/node.rs | 37 ++++++++++++++++--------------------- src/subgraph.rs | 35 +++++++++++++++-------------------- 3 files changed, 52 insertions(+), 69 deletions(-) diff --git a/src/edge.rs b/src/edge.rs index b9625bb..2faa389 100644 --- a/src/edge.rs +++ b/src/edge.rs @@ -29,49 +29,42 @@ impl Edge { } } - pub fn label(&mut self, label: &str) -> Self { - let mut edge = self.clone(); - edge.label = String::from(label); - edge + pub fn label(mut self, label: &str) -> Self { + self.label = String::from(label); + self } - pub fn style(&mut self, style: Style) -> Self { - let mut edge = self.clone(); - edge.style = style; - edge + pub fn style(mut self, style: Style) -> Self { + self.style = style; + self } - pub fn color(&mut self, color: Option<&str>) -> Self { - let mut edge = self.clone(); - edge.color = match color { + pub fn color(mut self, color: Option<&str>) -> Self { + self.color = match color { Some(c) => Some(String::from(c)), None => None, }; - edge + self } - pub fn start_arrow(&mut self, arrow: Arrow) -> Self { - let mut edge = self.clone(); - edge.start_arrow = arrow; - edge + pub fn start_arrow(mut self, arrow: Arrow) -> Self { + self.start_arrow = arrow; + self } - pub fn end_arrow(&mut self, arrow: Arrow) -> Self { - let mut edge = self.clone(); - edge.end_arrow = arrow; - edge + pub fn end_arrow(mut self, arrow: Arrow) -> Self { + self.end_arrow = arrow; + self } - pub fn label_url(&mut self, url: String) -> Self { - let mut edge = self.clone(); - edge.label_url = url; - edge + pub fn label_url(mut self, url: String) -> Self { + self.label_url = url; + self } - pub fn url(&mut self, url: String) -> Self { - let mut edge = self.clone(); - edge.url = url; - edge + pub fn url(mut self, url: String) -> Self { + self.url = url; + self } pub fn to_dot_string(&self, edge_symbol: &str) -> String { diff --git a/src/node.rs b/src/node.rs index 862074a..ff4b838 100644 --- a/src/node.rs +++ b/src/node.rs @@ -25,40 +25,35 @@ impl Node { } } - pub fn label(&self, label: &str) -> Self { - let mut node = self.clone(); - node.label = String::from(label); - node + pub fn label(mut self, label: &str) -> Self { + self.label = String::from(label); + self } - pub fn style(&self, style: Style) -> Self { - let mut node = self.clone(); - node.style = style; - node + pub fn style(mut self, style: Style) -> Self { + self.style = style; + self } - pub fn shape(&self, shape: Option<&str>) -> Self { - let mut node = self.clone(); + pub fn shape(mut self, shape: Option<&str>) -> Self { match shape { - Some(s) => node.shape = Some(String::from(s)), - None => node.shape = None, + Some(s) => self.shape = Some(String::from(s)), + None => self.shape = None, } - node + self } - pub fn color(&self, color: Option<&str>) -> Self { - let mut node = self.clone(); - node.color = match color { + pub fn color(mut self, color: Option<&str>) -> Self { + self.color = match color { Some(c) => Some(String::from(c)), None => None, }; - node + self } - pub fn url(&mut self, url: String) -> Self { - let mut node = self.clone(); - node.url = url; - node + pub fn url(mut self, url: String) -> Self { + self.url = url; + self } pub fn to_dot_string(&self) -> String { diff --git a/src/subgraph.rs b/src/subgraph.rs index f819f35..6c4c9ec 100644 --- a/src/subgraph.rs +++ b/src/subgraph.rs @@ -39,37 +39,32 @@ impl Subgraph { self.edges.push(edge); } - pub fn label(&self, label: &str) -> Self { - let mut subg = self.clone(); - subg.label = String::from(label); - subg + pub fn label(mut self, label: &str) -> Self { + self.label = String::from(label); + self } - pub fn style(&self, style: Style) -> Self { - let mut subg = self.clone(); - subg.style = style; - subg + pub fn style(mut self, style: Style) -> Self { + self.style = style; + self } - pub fn color(&self, color: Option<&str>) -> Self { - let mut subg = self.clone(); - subg.color = match color { + pub fn color(mut self, color: Option<&str>) -> Self { + self.color = match color { Some(c) => Some(String::from(c)), None => None, }; - subg + self } - pub fn edgeop(&self, edgeop: &str) -> Self { - let mut subg = self.clone(); - subg.edgeop = String::from(edgeop); - subg + pub fn edgeop(mut self, edgeop: &str) -> Self { + self.edgeop = String::from(edgeop); + self } - pub fn url(&mut self, url: String) -> Self { - let mut sub_graph = self.clone(); - sub_graph.url = url; - sub_graph + pub fn url(mut self, url: String) -> Self { + self.url = url; + self } pub fn to_dot_string(&self) -> String {