Skip to content

Commit

Permalink
Merge pull request #9 from ianakotey/main
Browse files Browse the repository at this point in the history
Added Url attribute to graph elements
  • Loading branch information
SelamaAshalanore authored Feb 27, 2023
2 parents 2109eb2 + 5799050 commit 3fa9c70
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 18 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ Cargo.lock

# .dot file for tests
**/*.dot

# Editor related configuratigurations
.vscode
44 changes: 39 additions & 5 deletions src/edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand Down Expand Up @@ -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 = &quote_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 = &quote_string(self.label_url.clone());
let escaped_url: &String = &quote_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=\"");
Expand Down
24 changes: 17 additions & 7 deletions src/graph.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
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<Node>,
edges: Vec<Edge>,
subgraph: Vec<Subgraph>
}

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) -> () {
Expand All @@ -31,6 +33,12 @@ impl Graph {
self.subgraph.push(subgraph.edgeop(self.kind.edgeop()))
}

pub fn url(&mut self, url: String) -> Self {
let mut graph = self.clone();
graph.url = url;
graph
}

pub fn to_dot_string(&self) -> io::Result<String> {
let mut writer = Vec::new();
self.render_opts(&mut writer).unwrap();
Expand All @@ -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: Write>(w: &mut W, arg: &[&str]) -> io::Result<()> {
for &s in arg {
w.write_all(s.as_bytes())?;
Expand All @@ -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![];
Expand Down
22 changes: 18 additions & 4 deletions src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ pub struct Node {
label: String,
style: Style,
color: Option<String>,
shape: Option<String>
shape: Option<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 }
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 {
Expand Down Expand Up @@ -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());
Expand Down
17 changes: 16 additions & 1 deletion src/subgraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ pub struct Subgraph {
style: Style,
color: Option<String>,
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) -> () {
Expand Down Expand Up @@ -61,9 +62,23 @@ impl Subgraph {
subg
}

pub fn url(&mut self, url: String) -> Self {
let mut sub_graph = self.clone();
sub_graph.url = url;
sub_graph
}

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 ");
Expand Down
64 changes: 63 additions & 1 deletion tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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").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"][URL="https://example.com/"];
}
"#);
}

#[test]
fn dot_in_node_name() {
let mut graph = Graph::new("single_node", Kind::Digraph);
Expand Down Expand Up @@ -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);
Expand All @@ -268,6 +281,7 @@ r#"digraph di {
"N1"[label="N1"];
}
subgraph cluster_1 {
URL="https://example.com/";
label="";
"N2"[label="N2"];
"N3"[label="N3"];
Expand Down Expand Up @@ -420,4 +434,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/"];
}
"#);
}
}

0 comments on commit 3fa9c70

Please sign in to comment.