summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorseancarroll <seanc28@gmail.com>2021-01-08 23:30:54 -0600
committerseancarroll <seanc28@gmail.com>2021-01-08 23:30:54 -0600
commit16bf6f3f1a132c61e40a0137d69c1b53bbf7bd59 (patch)
treec5db993abe1e24a2ef068cb3b13d589765471ad7
parente1ff0f4992d896c9e5b47df079c1a3b33e49cd01 (diff)
downloaddotavious-16bf6f3f1a132c61e40a0137d69c1b53bbf7bd59.zip
working through how to make it easier to render DOT to string
-rw-r--r--src/dot.rs63
-rw-r--r--src/lib.rs16
-rw-r--r--tests/dot.rs12
3 files changed, 65 insertions, 26 deletions
diff --git a/src/dot.rs b/src/dot.rs
index 0fe6796..634f24b 100644
--- a/src/dot.rs
+++ b/src/dot.rs
@@ -11,6 +11,7 @@ use std::borrow::Cow;
use std::collections::HashMap;
use std::io;
use std::io::prelude::*;
+use std::fmt::{Display, Formatter, Debug};
static INDENT: &str = " ";
@@ -35,8 +36,6 @@ impl<'a> Dot<'a> {
self.render_opts(w, &[])
}
- // io::Result<()> vs Result<(), Box<dyn Error>>
- // https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#the--operator-can-be-used-in-functions-that-return-result
/// Renders directed graph `g` into the writer `w` in DOT syntax.
/// (Main entry point for the library.)
// pub fn render_opts<W>(self, graph: Graph, w: &mut W, options: &[RenderOption]) -> io::Result<()>
@@ -44,34 +43,45 @@ impl<'a> Dot<'a> {
where
W: Write,
{
- if let Some(comment) = &self.graph.comment {
+ self.internal_render(&self.graph, w)
+ }
+
+ fn internal_render<W>(
+ &self,
+ graph: &Graph,
+ w: &mut W
+ ) -> io::Result<()>
+ where
+ W: Write,
+ {
+ if let Some(comment) = &graph.comment {
// TODO: split comment into lines of 80 or so characters
writeln!(w, "// {}", comment)?;
}
- let edge_op = &self.graph.edge_op();
- let strict = if self.graph.strict { "strict " } else { "" };
- write!(w, "{}{}", strict, &self.graph.graph_type())?;
+ let edge_op = &graph.edge_op();
+ let strict = if graph.strict { "strict " } else { "" };
+ write!(w, "{}{}", strict, &graph.graph_type())?;
- if let Some(id) = &self.graph.id {
+ if let Some(id) = &graph.id {
write!(w, " {}", id)?;
}
writeln!(w, " {{")?;
- if let Some(graph_attributes) = self.graph.graph_attributes {
+ if let Some(graph_attributes) = &graph.graph_attributes {
write!(w, "{}{}\n", INDENT, graph_attributes.dot_string())?;
}
- if let Some(node_attributes) = self.graph.node_attributes {
+ if let Some(node_attributes) = &graph.node_attributes {
write!(w, "{}{}\n", INDENT, node_attributes.dot_string())?;
}
- if let Some(edge_attributes) = self.graph.edge_attributes {
+ if let Some(edge_attributes) = &graph.edge_attributes {
write!(w, "{}{}\n", INDENT, edge_attributes.dot_string())?;
}
- for n in self.graph.nodes {
+ for n in &graph.nodes {
// TODO: handle render options
// Are render options something we need?
// we could clone the node or and remove the attributes based on render options
@@ -79,15 +89,15 @@ impl<'a> Dot<'a> {
writeln!(w, "{}{}", INDENT, n.dot_string())?;
}
- for e in self.graph.edges {
- let mut edge_source = e.source;
- if let Some(source_port_position) = e.source_port_position {
+ for e in &graph.edges {
+ let mut edge_source = e.source.to_owned();
+ if let Some(source_port_position) = &e.source_port_position {
edge_source
.push_str(format!(":{}", source_port_position.dot_string()).as_str())
}
- let mut edge_target = e.target;
- if let Some(target_port_position) = e.target_port_position {
+ let mut edge_target = e.target.to_owned();
+ if let Some(target_port_position) = &e.target_port_position {
edge_target
.push_str(format!(":{}", target_port_position.dot_string()).as_str())
}
@@ -113,15 +123,30 @@ impl<'a> Dot<'a> {
}
}
+impl<'a> Display for Dot<'a> {
+ fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
+ let mut writer= Vec::new();
+ self.internal_render(&self.graph, &mut writer).unwrap();
+
+ let mut s = String::new();
+ Read::read_to_string(&mut &*writer, &mut s).unwrap();
+
+ write!(f, "{}", s)?;
+
+ Ok(())
+ }
+}
+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum RenderOption {
NoEdgeLabels,
NoNodeLabels,
NoEdgeStyles,
NoNodeStyles,
-
- // TODO: replace with Fontname(String),
- Monospace,
+ /// Use indices for node labels.
+ NodeIndexLabel,
+ /// Use indices for edge labels.
+ EdgeIndexLabel,
}
/// Returns vec holding all the default render options.
diff --git a/src/lib.rs b/src/lib.rs
index f1999fa..65d98c2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,7 +18,7 @@
//! use std::io;
//! use std::io::Read;
//!
-//! let g = GraphBuilder::new_directed(Some("single_edge".to_string()))
+//! let g = GraphBuilder::new_directed(Some("example".to_string()))
//! .add_node(Node::new("N0".to_string()))
//! .add_node(Node::new("N1".to_string()))
//! .add_edge(Edge::new("N0".to_string(), "N1".to_string()))
@@ -32,12 +32,14 @@
//! let mut dot_string = String::new();
//! Read::read_to_string(&mut &*writer, &mut dot_string).unwrap();
//! println!("{}", dot_string);
-//!
-//! // digraph graph_attributes {
-//! // N0;
-//! // N1;
-//! // N0 -> N1;
-//! // }
+//! ```
+//! Produces
+//! ```dot
+//! digraph example {
+//! N0;
+//! N1;
+//! N0 -> N1;
+//! }
//! ```
diff --git a/tests/dot.rs b/tests/dot.rs
index fb97175..1ab467d 100644
--- a/tests/dot.rs
+++ b/tests/dot.rs
@@ -30,6 +30,18 @@ fn empty_digraph_without_id() {
}
#[test]
+fn display() {
+ let g = GraphBuilder::new_directed(None).build();
+ let dot = Dot { graph: g };
+ assert_eq!(
+ format!("{}", dot),
+ r#"digraph {
+}
+"#
+ );
+}
+
+#[test]
fn graph_comment() {
let g = GraphBuilder::new_directed(None)
.comment("Comment goes here")