diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index cd9e677d87f..4d07573268a 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -312,6 +312,40 @@ pub enum LabelText<'a> { EscStr(Cow<'a, str>), } +/// The style for a node or edge. +/// See http://www.graphviz.org/doc/info/attrs.html#k:style for descriptions. +/// Note that some of these are not valid for edges. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum Style { + None, + Solid, + Dashed, + Dotted, + Bold, + Rounded, + Diagonals, + Filled, + Striped, + Wedged, +} + +impl Style { + pub fn as_slice(self) -> &'static str { + match self { + Style::None => "", + Style::Solid => "solid", + Style::Dashed => "dashed", + Style::Dotted => "dotted", + Style::Bold => "bold", + Style::Rounded => "rounded", + Style::Diagonals => "diagonals", + Style::Filled => "filled", + Style::Striped => "striped", + Style::Wedged => "wedged", + } + } +} + // There is a tension in the design of the labelling API. // // For example, I considered making a `Labeller` trait that @@ -430,6 +464,16 @@ pub trait Labeller<'a,N,E> { let _ignored = e; LabelStr("".into_cow()) } + + /// Maps `n` to a style that will be used in the rendered output. + fn node_style(&'a self, _n: &N) -> Style { + Style::None + } + + /// Maps `e` to a style that will be used in the rendered output. + fn edge_style(&'a self, _e: &E) -> Style { + Style::None + } } impl<'a> LabelText<'a> { @@ -529,6 +573,8 @@ pub trait GraphWalk<'a, N, E> { pub enum RenderOption { NoEdgeLabels, NoNodeLabels, + NoEdgeStyles, + NoNodeStyles, } /// Returns vec holding all the default render options. @@ -562,30 +608,53 @@ pub fn render_opts<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N for n in g.nodes().iter() { try!(indent(w)); let id = g.node_id(n); - if options.contains(&RenderOption::NoNodeLabels) { - try!(writeln(w, &[id.as_slice(), ";"])); - } else { - let escaped = g.node_label(n).escape(); - try!(writeln(w, &[id.as_slice(), - "[label=\"", &escaped, "\"];"])); + + let escaped = &g.node_label(n).escape(); + + let mut text = vec![id.as_slice()]; + + if !options.contains(&RenderOption::NoNodeLabels) { + text.push("[label=\""); + text.push(escaped); + text.push("\"]"); } + + let style = g.node_style(n); + if !options.contains(&RenderOption::NoNodeStyles) && style != Style::None { + text.push("[style=\""); + text.push(style.as_slice()); + text.push("\"]"); + } + + text.push(";"); + try!(writeln(w, &text)); } for e in g.edges().iter() { - let escaped_label = g.edge_label(e).escape(); + let escaped_label = &g.edge_label(e).escape(); try!(indent(w)); let source = g.source(e); let target = g.target(e); let source_id = g.node_id(&source); let target_id = g.node_id(&target); - if options.contains(&RenderOption::NoEdgeLabels) { - try!(writeln(w, &[source_id.as_slice(), - " -> ", target_id.as_slice(), ";"])); - } else { - try!(writeln(w, &[source_id.as_slice(), - " -> ", target_id.as_slice(), - "[label=\"", &escaped_label, "\"];"])); + + let mut text = vec![source_id.as_slice(), " -> ", target_id.as_slice()]; + + if !options.contains(&RenderOption::NoEdgeLabels) { + text.push("[label=\""); + text.push(escaped_label); + text.push("\"]"); } + + let style = g.edge_style(e); + if !options.contains(&RenderOption::NoEdgeStyles) && style != Style::None { + text.push("[style=\""); + text.push(style.as_slice()); + text.push("\"]"); + } + + text.push(";"); + try!(writeln(w, &text)); } writeln(w, &["}"]) @@ -594,7 +663,7 @@ pub fn render_opts<'a, N:Clone+'a, E:Clone+'a, G:Labeller<'a,N,E>+GraphWalk<'a,N #[cfg(test)] mod tests { use self::NodeLabels::*; - use super::{Id, Labeller, Nodes, Edges, GraphWalk, render}; + use super::{Id, Labeller, Nodes, Edges, GraphWalk, render, Style}; use super::LabelText::{self, LabelStr, EscStr}; use std::io; use std::io::prelude::*; @@ -603,11 +672,14 @@ mod tests { /// each node is an index in a vector in the graph. type Node = usize; struct Edge { - from: usize, to: usize, label: &'static str + from: usize, + to: usize, + label: &'static str, + style: Style, } - fn edge(from: usize, to: usize, label: &'static str) -> Edge { - Edge { from: from, to: to, label: label } + fn edge(from: usize, to: usize, label: &'static str, style: Style) -> Edge { + Edge { from: from, to: to, label: label, style: style } } struct LabelledGraph { @@ -623,6 +695,8 @@ mod tests { /// text. node_labels: Vec>, + node_styles: Vec