diff options
author | seancarroll <seanc28@gmail.com> | 2021-01-08 23:30:54 -0600 |
---|---|---|
committer | seancarroll <seanc28@gmail.com> | 2021-01-08 23:30:54 -0600 |
commit | 16bf6f3f1a132c61e40a0137d69c1b53bbf7bd59 (patch) | |
tree | c5db993abe1e24a2ef068cb3b13d589765471ad7 | |
parent | e1ff0f4992d896c9e5b47df079c1a3b33e49cd01 (diff) | |
download | dotavious-16bf6f3f1a132c61e40a0137d69c1b53bbf7bd59.zip |
working through how to make it easier to render DOT to string
-rw-r--r-- | src/dot.rs | 63 | ||||
-rw-r--r-- | src/lib.rs | 16 | ||||
-rw-r--r-- | tests/dot.rs | 12 |
3 files changed, 65 insertions, 26 deletions
@@ -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. @@ -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") |