(index<- )        ./libgraphviz/lib.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   1  // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
   2  // file at the top-level directory of this distribution and at
   3  // http://rust-lang.org/COPYRIGHT.
   4  //
   5  // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
   6  // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
   7  // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
   8  // option. This file may not be copied, modified, or distributed
   9  // except according to those terms.
  10  
  11  /*! Generate files suitable for use with [Graphviz](http://www.graphviz.org/)
  12  
  13  The `render` function generates output (e.g. an `output.dot` file) for
  14  use with [Graphviz](http://www.graphviz.org/) by walking a labelled
  15  graph. (Graphviz can then automatically lay out the nodes and edges
  16  of the graph, and also optionally render the graph as an image or
  17  other [output formats](
  18  http://www.graphviz.org/content/output-formats), such as SVG.)
  19  
  20  Rather than impose some particular graph data structure on clients,
  21  this library exposes two traits that clients can implement on their
  22  own structs before handing them over to the rendering function.
  23  
  24  Note: This library does not yet provide access to the full
  25  expressiveness of the [DOT language](
  26  http://www.graphviz.org/doc/info/lang.html). For example, there are
  27  many [attributes](http://www.graphviz.org/content/attrs) related to
  28  providing layout hints (e.g. left-to-right versus top-down, which
  29  algorithm to use, etc). The current intention of this library is to
  30  emit a human-readable .dot file with very regular structure suitable
  31  for easy post-processing.
  32  
  33  # Examples
  34  
  35  The first example uses a very simple graph representation: a list of
  36  pairs of ints, representing the edges (the node set is implicit).
  37  Each node label is derived directly from the int representing the node,
  38  while the edge labels are all empty strings.
  39  
  40  This example also illustrates how to use `MaybeOwnedVector` to return
  41  an owned vector or a borrowed slice as appropriate: we construct the
  42  node vector from scratch, but borrow the edge list (rather than
  43  constructing a copy of all the edges from scratch).
  44  
  45  The output from this example renders five nodes, with the first four
  46  forming a diamond-shaped acyclic graph and then pointing to the fifth
  47  which is cyclic.
  48  
  49  ```rust
  50  use dot = graphviz;
  51  use graphviz::maybe_owned_vec::IntoMaybeOwnedVector;
  52  
  53  type Nd = int;
  54  type Ed = (int,int);
  55  struct Edges(Vec<Ed>);
  56  
  57  pub fn render_to<W:Writer>(output: &mut W) {
  58      let edges = Edges(vec!((0,1), (0,2), (1,3), (2,3), (3,4), (4,4)));
  59      dot::render(&edges, output).unwrap()
  60  }
  61  
  62  impl<'a> dot::Labeller<'a, Nd, Ed> for Edges {
  63      fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example1") }
  64  
  65      fn node_id(&'a self, n: &Nd) -> dot::Id<'a> {
  66          dot::Id::new(format!("N{}", *n))
  67      }
  68  }
  69  
  70  impl<'a> dot::GraphWalk<'a, Nd, Ed> for Edges {
  71      fn nodes(&self) -> dot::Nodes<'a,Nd> {
  72          // (assumes that |N| \approxeq |E|)
  73          let &Edges(ref v) = self;
  74          let mut nodes = Vec::with_capacity(v.len());
  75          for &(s,t) in v.iter() {
  76              nodes.push(s); nodes.push(t);
  77          }
  78          nodes.sort();
  79          nodes.dedup();
  80          nodes.into_maybe_owned()
  81      }
  82  
  83      fn edges(&'a self) -> dot::Edges<'a,Ed> {
  84          let &Edges(ref edges) = self;
  85          edges.as_slice().into_maybe_owned()
  86      }
  87  
  88      fn source(&self, e: &Ed) -> Nd { let &(s,_) = e; s }
  89  
  90      fn target(&self, e: &Ed) -> Nd { let &(_,t) = e; t }
  91  }
  92  
  93  # pub fn main() { use std::io::MemWriter; render_to(&mut MemWriter::new()) }
  94  ```
  95  
  96  ```no_run
  97  # pub fn render_to<W:Writer>(output: &mut W) { unimplemented!() }
  98  pub fn main() {
  99      use std::io::File;
 100      let mut f = File::create(&Path::new("example1.dot"));
 101      render_to(&mut f)
 102  }
 103  ```
 104  
 105  Output from first example (in `example1.dot`):
 106  
 107  ```ignore
 108  digraph example1 {
 109      N0[label="N0"];
 110      N1[label="N1"];
 111      N2[label="N2"];
 112      N3[label="N3"];
 113      N4[label="N4"];
 114      N0 -> N1[label=""];
 115      N0 -> N2[label=""];
 116      N1 -> N3[label=""];
 117      N2 -> N3[label=""];
 118      N3 -> N4[label=""];
 119      N4 -> N4[label=""];
 120  }
 121  ```
 122  
 123  The second example illustrates using `node_label` and `edge_label` to
 124  add labels to the nodes and edges in the rendered graph. The graph
 125  here carries both `nodes` (the label text to use for rendering a
 126  particular node), and `edges` (again a list of `(source,target)`
 127  indices).
 128  
 129  This example also illustrates how to use a type (in this case the edge
 130  type) that shares substructure with the graph: the edge type here is a
 131  direct reference to the `(source,target)` pair stored in the graph's
 132  internal vector (rather than passing around a copy of the pair
 133  itself). Note that this implies that `fn edges(&'a self)` must
 134  construct a fresh `Vec<&'a (uint,uint)>` from the `Vec<(uint,uint)>`
 135  edges stored in `self`.
 136  
 137  Since both the set of nodes and the set of edges are always
 138  constructed from scratch via iterators, we use the `collect()` method
 139  from the `Iterator` trait to collect the nodes and edges into freshly
 140  constructed growable `Vec` values (rather use the `into_maybe_owned`
 141  from the `IntoMaybeOwnedVector` trait as was used in the first example
 142  above).
 143  
 144  The output from this example renders four nodes that make up the
 145  Hasse-diagram for the subsets of the set `{x, y}`. Each edge is
 146  labelled with the &sube; character (specified using the HTML character
 147  entity `&sube`).
 148  
 149  ```rust
 150  use dot = graphviz;
 151  use std::str;
 152  
 153  type Nd = uint;
 154  type Ed<'a> = &'a (uint, uint);
 155  struct Graph { nodes: Vec<&'static str>, edges: Vec<(uint,uint)> }
 156  
 157  pub fn render_to<W:Writer>(output: &mut W) {
 158      let nodes = vec!("{x,y}","{x}","{y}","{}");
 159      let edges = vec!((0,1), (0,2), (1,3), (2,3));
 160      let graph = Graph { nodes: nodes, edges: edges };
 161  
 162      dot::render(&graph, output).unwrap()
 163  }
 164  
 165  impl<'a> dot::Labeller<'a, Nd, Ed<'a>> for Graph {
 166      fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example2") }
 167      fn node_id(&'a self, n: &Nd) -> dot::Id<'a> {
 168          dot::Id::new(format!("N{}", n))
 169      }
 170      fn node_label<'a>(&'a self, n: &Nd) -> dot::LabelText<'a> {
 171          dot::LabelStr(str::Slice(self.nodes.get(*n).as_slice()))
 172      }
 173      fn edge_label<'a>(&'a self, _: &Ed) -> dot::LabelText<'a> {
 174          dot::LabelStr(str::Slice("&sube;"))
 175      }
 176  }
 177  
 178  impl<'a> dot::GraphWalk<'a, Nd, Ed<'a>> for Graph {
 179      fn nodes(&self) -> dot::Nodes<'a,Nd> { range(0,self.nodes.len()).collect() }
 180      fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> { self.edges.iter().collect() }
 181      fn source(&self, e: &Ed) -> Nd { let & &(s,_) = e; s }
 182      fn target(&self, e: &Ed) -> Nd { let & &(_,t) = e; t }
 183  }
 184  
 185  # pub fn main() { use std::io::MemWriter; render_to(&mut MemWriter::new()) }
 186  ```
 187  
 188  ```no_run
 189  # pub fn render_to<W:Writer>(output: &mut W) { unimplemented!() }
 190  pub fn main() {
 191      use std::io::File;
 192      let mut f = File::create(&Path::new("example2.dot"));
 193      render_to(&mut f)
 194  }
 195  ```
 196  
 197  The third example is similar to the second, except now each node and
 198  edge now carries a reference to the string label for each node as well
 199  as that node's index. (This is another illustration of how to share
 200  structure with the graph itself, and why one might want to do so.)
 201  
 202  The output from this example is the same as the second example: the
 203  Hasse-diagram for the subsets of the set `{x, y}`.
 204  
 205  ```rust
 206  use dot = graphviz;
 207  use std::str;
 208  
 209  type Nd<'a> = (uint, &'a str);
 210  type Ed<'a> = (Nd<'a>, Nd<'a>);
 211  struct Graph { nodes: Vec<&'static str>, edges: Vec<(uint,uint)> }
 212  
 213  pub fn render_to<W:Writer>(output: &mut W) {
 214      let nodes = vec!("{x,y}","{x}","{y}","{}");
 215      let edges = vec!((0,1), (0,2), (1,3), (2,3));
 216      let graph = Graph { nodes: nodes, edges: edges };
 217  
 218      dot::render(&graph, output).unwrap()
 219  }
 220  
 221  impl<'a> dot::Labeller<'a, Nd<'a>, Ed<'a>> for Graph {
 222      fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new("example3") }
 223      fn node_id(&'a self, n: &Nd<'a>) -> dot::Id<'a> {
 224          dot::Id::new(format!("N{:u}", n.val0()))
 225      }
 226      fn node_label<'a>(&'a self, n: &Nd<'a>) -> dot::LabelText<'a> {
 227          let &(i, _) = n;
 228          dot::LabelStr(str::Slice(self.nodes.get(i).as_slice()))
 229      }
 230      fn edge_label<'a>(&'a self, _: &Ed<'a>) -> dot::LabelText<'a> {
 231          dot::LabelStr(str::Slice("&sube;"))
 232      }
 233  }
 234  
 235  impl<'a> dot::GraphWalk<'a, Nd<'a>, Ed<'a>> for Graph {
 236      fn nodes(&'a self) -> dot::Nodes<'a,Nd<'a>> {
 237          self.nodes.iter().map(|s|s.as_slice()).enumerate().collect()
 238      }
 239      fn edges(&'a self) -> dot::Edges<'a,Ed<'a>> {
 240          self.edges.iter()
 241              .map(|&(i,j)|((i, self.nodes.get(i).as_slice()),
 242                            (j, self.nodes.get(j).as_slice())))
 243              .collect()
 244      }
 245      fn source(&self, e: &Ed<'a>) -> Nd<'a> { let &(s,_) = e; s }
 246      fn target(&self, e: &Ed<'a>) -> Nd<'a> { let &(_,t) = e; t }
 247  }
 248  
 249  # pub fn main() { use std::io::MemWriter; render_to(&mut MemWriter::new()) }
 250  ```
 251  
 252  ```no_run
 253  # pub fn render_to<W:Writer>(output: &mut W) { unimplemented!() }
 254  pub fn main() {
 255      use std::io::File;
 256      let mut f = File::create(&Path::new("example3.dot"));
 257      render_to(&mut f)
 258  }
 259  ```
 260  
 261  # References
 262  
 263  * [Graphviz](http://www.graphviz.org/)
 264  
 265  * [DOT language](http://www.graphviz.org/doc/info/lang.html)
 266  
 267  */
 268  
 269  #![crate_id = "graphviz#0.11-pre"]
 270  #![crate_type = "rlib"]
 271  #![crate_type = "dylib"]
 272  #![license = "MIT/ASL2"]
 273  #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
 274         html_favicon_url = "http://www.rust-lang.org/favicon.ico",
 275         html_root_url = "http://static.rust-lang.org/doc/master")]
 276  
 277  #![experimental]
 278  
 279  use std::io;
 280  use std::str;
 281  use self::maybe_owned_vec::MaybeOwnedVector;
 282  
 283  pub mod maybe_owned_vec;
 284  
 285  /// The text for a graphviz label on a node or edge.
 286  pub enum LabelText<'a> {
 287      /// This kind of label preserves the text directly as is.
 288      ///
 289      /// Occurrences of backslashes (`\`) are escaped, and thus appear
 290      /// as backslashes in the rendered label.
 291      LabelStr(str::MaybeOwned<'a>),
 292  
 293      /// This kind of label uses the graphviz label escString type:
 294      /// http://www.graphviz.org/content/attrs#kescString
 295      ///
 296      /// Occurrences of backslashes (`\`) are not escaped; instead they
 297      /// are interpreted as initiating an escString escape sequence.
 298      ///
 299      /// Escape sequences of particular interest: in addition to `\n`
 300      /// to break a line (centering the line preceding the `\n`), there
 301      /// are also the escape sequences `\l` which left-justifies the
 302      /// preceding line and `\r` which right-justifies it.
 303      EscStr(str::MaybeOwned<'a>),
 304  }
 305  
 306  // There is a tension in the design of the labelling API.
 307  //
 308  // For example, I considered making a `Labeller<T>` trait that
 309  // provides labels for `T`, and then making the graph type `G`
 310  // implement `Labeller<Node>` and `Labeller<Edge>`. However, this is
 311  // not possible without functional dependencies. (One could work
 312  // around that, but I did not explore that avenue heavily.)
 313  //
 314  // Another approach that I actually used for a while was to make a
 315  // `Label<Context>` trait that is implemented by the client-specific
 316  // Node and Edge types (as well as an implementation on Graph itself
 317  // for the overall name for the graph). The main disadvantage of this
 318  // second approach (compared to having the `G` type parameter
 319  // implement a Labelling service) that I have encountered is that it
 320  // makes it impossible to use types outside of the current crate
 321  // directly as Nodes/Edges; you need to wrap them in newtype'd
 322  // structs. See e.g. the `No` and `Ed` structs in the examples. (In
 323  // practice clients using a graph in some other crate would need to
 324  // provide some sort of adapter shim over the graph anyway to
 325  // interface with this library).
 326  //
 327  // Another approach would be to make a single `Labeller<N,E>` trait
 328  // that provides three methods (graph_label, node_label, edge_label),
 329  // and then make `G` implement `Labeller<N,E>`. At first this did not
 330  // appeal to me, since I had thought I would need separate methods on
 331  // each data variant for dot-internal identifiers versus user-visible
 332  // labels. However, the identifier/label distinction only arises for
 333  // nodes; graphs themselves only have identifiers, and edges only have
 334  // labels.
 335  //
 336  // So in the end I decided to use the third approach described above.
 337  
 338  /// `Id` is a Graphviz `ID`.
 339  pub struct Id<'a> {
 340      name: str::MaybeOwned<'a>,
 341  }
 342  
 343  impl<'a> Id<'a> {
 344      /// Creates an `Id` named `name`.
 345      ///
 346      /// The caller must ensure that the input conforms to an
 347      /// identifier format: it must be a non-empty string made up of
 348      /// alphanumeric or underscore characters, not beginning with a
 349      /// digit (i.e. the regular expression `[a-zA-Z_][a-zA-Z_0-9]*`).
 350      ///
 351      /// (Note: this format is a strict subset of the `ID` format
 352      /// defined by the DOT language.  This function may change in the
 353      /// future to accept a broader subset, or the entirety, of DOT's
 354      /// `ID` format.)
 355      pub fn new<Name:str::IntoMaybeOwned<'a>>(nameName) -> Id<'a> {
 356          let name = name.into_maybe_owned();
 357          {
 358              let mut chars = name.as_slice().chars();
 359              assert!(is_letter_or_underscore(chars.next().unwrap()));
 360              assert!(chars.all(is_constituent));
 361          }
 362          return Id{ name: name };
 363  
 364          fn is_letter_or_underscore(c: char) -> bool {
 365              in_range('a', c, 'z') || in_range('A', c, 'Z') || c == '_'
 366          }
 367          fn is_constituent(cchar) -> bool {
 368              is_letter_or_underscore(c) || in_range('0', c, '9')
 369          }
 370          fn in_range(lowchar, cchar, highchar) -> bool {
 371              low as uint <= c as uint && c as uint <= high as uint
 372          }
 373      }
 374  
 375      pub fn as_slice(&'a self) -> &'a str {
 376          self.name.as_slice()
 377      }
 378  
 379      pub fn name(self) -> str::MaybeOwned<'a> {
 380          self.name
 381      }
 382  }
 383  
 384  /// Each instance of a type that implements `Label<C>` maps to a
 385  /// unique identifier with respect to `C`, which is used to identify
 386  /// it in the generated .dot file. They can also provide more
 387  /// elaborate (and non-unique) label text that is used in the graphviz
 388  /// rendered output.
 389  
 390  /// The graph instance is responsible for providing the DOT compatible
 391  /// identifiers for the nodes and (optionally) rendered labels for the nodes and
 392  /// edges, as well as an identifier for the graph itself.
 393  pub trait Labeller<'a,N,E> {
 394      /// Must return a DOT compatible identifier naming the graph.
 395      fn graph_id(&'a self) -> Id<'a>;
 396  
 397      /// Maps `n` to a unique identifier with respect to `self`. The
 398      /// implementor is responsible for ensuring that the returned name
 399      /// is a valid DOT identifier.
 400      fn node_id(&'a self, n: &N) -> Id<'a>;
 401  
 402      /// Maps `n` to a label that will be used in the rendered output.
 403      /// The label need not be unique, and may be the empty string; the
 404      /// default is just the output from `node_id`.
 405      fn node_label(&'a self, n&N) -> LabelText<'a> {
 406          LabelStr(self.node_id(n).name)
 407      }
 408  
 409      /// Maps `e` to a label that will be used in the rendered output.
 410      /// The label need not be unique, and may be the empty string; the
 411      /// default is in fact the empty string.
 412      fn edge_label(&'a self, e&E) -> LabelText<'a> {
 413          let _ignored = e;
 414          LabelStr(str::Slice(""))
 415      }
 416  }
 417  
 418  impl<'a> LabelText<'a> {
 419      fn escape_char(cchar, f|char|) {
 420          match c {
 421              // not escaping \\, since Graphviz escString needs to
 422              // interpret backslashes; see EscStr above.
 423              '\\' => f(c),
 424              _ => c.escape_default(f)
 425          }
 426      }
 427      fn escape_str(s&str) -> StrBuf {
 428          let mut out = StrBuf::with_capacity(s.len());
 429          for c in s.chars() {
 430              LabelText::escape_char(c, |c| out.push_char(c));
 431          }
 432          out
 433      }
 434  
 435      /// Renders text as string suitable for a label in a .dot file.
 436      pub fn escape(&self) -> ~str {
 437          match self {
 438              &LabelStr(ref s) => s.as_slice().escape_default(),
 439              &EscStr(ref s) => LabelText::escape_str(s.as_slice()).into_owned(),
 440          }
 441      }
 442  }
 443  
 444  pub type Nodes<'a,N> = MaybeOwnedVector<'a,N>;
 445  pub type Edges<'a,E> = MaybeOwnedVector<'a,E>;
 446  
 447  // (The type parameters in GraphWalk should be associated items,
 448  // when/if Rust supports such.)
 449  
 450  /// GraphWalk is an abstraction over a directed graph = (nodes,edges)
 451  /// made up of node handles `N` and edge handles `E`, where each `E`
 452  /// can be mapped to its source and target nodes.
 453  ///
 454  /// The lifetime parameter `'a` is exposed in this trait (rather than
 455  /// introduced as a generic parameter on each method declaration) so
 456  /// that a client impl can choose `N` and `E` that have substructure
 457  /// that is bound by the self lifetime `'a`.
 458  ///
 459  /// The `nodes` and `edges` method each return instantiations of
 460  /// `MaybeOwnedVector` to leave implementors the freedom to create
 461  /// entirely new vectors or to pass back slices into internally owned
 462  /// vectors.
 463  pub trait GraphWalk<'a, N, E> {
 464      /// Returns all the nodes in this graph.
 465      fn nodes(&'a self) -> Nodes<'a, N>;
 466      /// Returns all of the edges in this graph.
 467      fn edges(&'a self) -> Edges<'a, E>;
 468      /// The source node for `edge`.
 469      fn source(&'a self, edge: &E) -> N;
 470      /// The target node for `edge`.
 471      fn target(&'a self, edge: &E) -> N;
 472  }
 473  
 474  /// Renders directed graph `g` into the writer `w` in DOT syntax.
 475  /// (Main entry point for the library.)
 476  pub fn render<'a, N, E, G:Labeller<'a,N,E>+GraphWalk<'a,N,E>, W:Writer>(
 477                g: &'a G,
 478                w: &mut W) -> io::IoResult<()>
 479  {
 480      fn writeln<W:Writer>(w: &mut W, arg: &[&str]) -> io::IoResult<()> {
 481          for &s in arg.iter() { try!(w.write_str(s)); }
 482          w.write_char('\n')
 483      }
 484  
 485      fn indent<W:Writer>(w: &mut W) -> io::IoResult<()> {
 486          w.write_str("    ")
 487      }
 488  
 489      try!(writeln(w, ["digraph ", g.graph_id().as_slice(), {"]));
 490      for n in g.nodes().iter() {
 491          try!(indent(w));
 492          let id = g.node_id(n);
 493          let escaped = g.node_label(n).escape();
 494          try!(writeln(w, [id.as_slice(),
 495                           "[label=\"", escaped.as_slice(), "\"];"]));
 496      }
 497  
 498      for e in g.edges().iter() {
 499          let escaped_label = g.edge_label(e).escape();
 500          try!(indent(w));
 501          let source = g.source(e);
 502          let target = g.target(e);
 503          let source_id = g.node_id(&source);
 504          let target_id = g.node_id(&target);
 505          try!(writeln(w, [source_id.as_slice(), -> ", target_id.as_slice(),
 506                           "[label=\"", escaped_label.as_slice(), "\"];"]));
 507      }
 508  
 509      writeln(w, ["}"])
 510  }
 511  
 512  #[cfg(test)]
 513  mod tests {
 514      use super::{Id, LabelText, LabelStr, EscStr, Labeller};
 515      use super::{Nodes, Edges, GraphWalk, render};
 516      use std::io::{MemWriter, BufReader, IoResult};
 517      use std::str;
 518  
 519      /// each node is an index in a vector in the graph.
 520      type Node = uint;
 521      struct Edge {
 522          from: uint, to: uint, label: &'static str
 523      }
 524  
 525      fn Edge(from: uint, to: uint, label: &'static str) -> Edge {
 526          Edge { from: from, to: to, label: label }
 527      }
 528  
 529      struct LabelledGraph {
 530          /// The name for this graph. Used for labelling generated `digraph`.
 531          name: &'static str,
 532  
 533          /// Each node is an index into `node_labels`; these labels are
 534          /// used as the label text for each node. (The node *names*,
 535          /// which are unique identifiers, are derived from their index
 536          /// in this array.)
 537          ///
 538          /// If a node maps to None here, then just use its name as its
 539          /// text.
 540          node_labels: Vec<Option<&'static str>>,
 541  
 542          /// Each edge relates a from-index to a to-index along with a
 543          /// label; `edges` collects them.
 544          edges: Vec<Edge>,
 545      }
 546  
 547      // A simple wrapper around LabelledGraph that forces the labels to
 548      // be emitted as EscStr.
 549      struct LabelledGraphWithEscStrs {
 550          graph: LabelledGraph
 551      }
 552  
 553      enum NodeLabels<L> {
 554          AllNodesLabelled(Vec<L>),
 555          UnlabelledNodes(uint),
 556          SomeNodesLabelled(Vec<Option<L>>),
 557      }
 558  
 559      type Trivial = NodeLabels<&'static str>;
 560  
 561      impl NodeLabels<&'static str> {
 562          fn to_opt_strs(self) -> Vec<Option<&'static str>{
 563              match self {
 564                  UnlabelledNodes(len)
 565                      => Vec::from_elem(len, None).move_iter().collect(),
 566                  AllNodesLabelled(lbls)
 567                      => lbls.move_iter().map(
 568                          |l|Some(l)).collect(),
 569                  SomeNodesLabelled(lbls)
 570                      => lbls.move_iter().collect(),
 571              }
 572          }
 573      }
 574  
 575      impl LabelledGraph {
 576          fn new(name: &'static str,
 577                 node_labels: Trivial,
 578                 edges: Vec<Edge>) -> LabelledGraph {
 579              LabelledGraph {
 580                  name: name,
 581                  node_labels: node_labels.to_opt_strs(),
 582                  edges: edges
 583              }
 584          }
 585      }
 586  
 587      impl LabelledGraphWithEscStrs {
 588          fn new(name: &'static str,
 589                 node_labels: Trivial,
 590                 edges: Vec<Edge>) -> LabelledGraphWithEscStrs {
 591              LabelledGraphWithEscStrs {
 592                  graph: LabelledGraph::new(name, node_labels, edges)
 593              }
 594          }
 595      }
 596  
 597      fn id_name<'a>(n: &Node) -> Id<'a> {
 598          Id::new(format!("N{:u}", *n))
 599      }
 600  
 601      impl<'a> Labeller<'a, Node, &'a Edge> for LabelledGraph {
 602          fn graph_id(&'a self) -> Id<'a> {
 603              Id::new(self.name.as_slice())
 604          }
 605          fn node_id(&'a self, n: &Node) -> Id<'a> {
 606              id_name(n)
 607          }
 608          fn node_label(&'a self, n: &Node) -> LabelText<'a> {
 609              match self.node_labels.get(*n) {
 610                  &Some(ref l) => LabelStr(str::Slice(l.as_slice())),
 611                  &None        => LabelStr(id_name(n).name()),
 612              }
 613          }
 614          fn edge_label(&'a self, e: & &'a Edge) -> LabelText<'a> {
 615              LabelStr(str::Slice(e.label.as_slice()))
 616          }
 617      }
 618  
 619      impl<'a> Labeller<'a, Node, &'a Edge> for LabelledGraphWithEscStrs {
 620          fn graph_id(&'a self) -> Id<'a> { self.graph.graph_id() }
 621          fn node_id(&'a self, n: &Node) -> Id<'a> { self.graph.node_id(n) }
 622          fn node_label(&'a self, n: &Node) -> LabelText<'a> {
 623              match self.graph.node_label(n) {
 624                  LabelStr(s) | EscStr(s) => EscStr(s),
 625              }
 626          }
 627          fn edge_label(&'a self, e: & &'a Edge) -> LabelText<'a> {
 628              match self.graph.edge_label(e) {
 629                  LabelStr(s) | EscStr(s) => EscStr(s),
 630              }
 631          }
 632      }
 633  
 634      impl<'a> GraphWalk<'a, Node, &'a Edge> for LabelledGraph {
 635          fn nodes(&'a self) -> Nodes<'a,Node> {
 636              range(0u, self.node_labels.len()).collect()
 637          }
 638          fn edges(&'a self) -> Edges<'a,&'a Edge> {
 639              self.edges.iter().collect()
 640          }
 641          fn source(&'a self, edge: & &'a Edge) -> Node {
 642              edge.from
 643          }
 644          fn target(&'a self, edge: & &'a Edge) -> Node {
 645              edge.to
 646          }
 647      }
 648  
 649      impl<'a> GraphWalk<'a, Node, &'a Edge> for LabelledGraphWithEscStrs {
 650          fn nodes(&'a self) -> Nodes<'a,Node> {
 651              self.graph.nodes()
 652          }
 653          fn edges(&'a self) -> Edges<'a,&'a Edge> {
 654              self.graph.edges()
 655          }
 656          fn source(&'a self, edge: & &'a Edge) -> Node {
 657              edge.from
 658          }
 659          fn target(&'a self, edge: & &'a Edge) -> Node {
 660              edge.to
 661          }
 662      }
 663  
 664      fn test_input(g: LabelledGraph) -> IoResult<~str> {
 665          let mut writer = MemWriter::new();
 666          render(&g, &mut writer).unwrap();
 667          let mut r = BufReader::new(writer.get_ref());
 668          r.read_to_str()
 669      }
 670  
 671      // All of the tests use raw-strings as the format for the expected outputs,
 672      // so that you can cut-and-paste the content into a .dot file yourself to
 673      // see what the graphviz visualizer would produce.
 674  
 675      #[test]
 676      fn empty_graph() {
 677          let labels : Trivial = UnlabelledNodes(0);
 678          let r = test_input(LabelledGraph::new("empty_graph", labels, vec!()));
 679          assert_eq!(r.unwrap().as_slice(),
 680  r#"digraph empty_graph {
 681  }
 682  "#);
 683      }
 684  
 685      #[test]
 686      fn single_node() {
 687          let labels : Trivial = UnlabelledNodes(1);
 688          let r = test_input(LabelledGraph::new("single_node", labels, vec!()));
 689          assert_eq!(r.unwrap().as_slice(),
 690  r#"digraph single_node {
 691      N0[label="N0"];
 692  }
 693  "#);
 694      }
 695  
 696      #[test]
 697      fn single_edge() {
 698          let labels : Trivial = UnlabelledNodes(2);
 699          let result = test_input(LabelledGraph::new("single_edge", labels,
 700                                                     vec!(Edge(0, 1, "E"))));
 701          assert_eq!(result.unwrap().as_slice(),
 702  r#"digraph single_edge {
 703      N0[label="N0"];
 704      N1[label="N1"];
 705      N0 -> N1[label="E"];
 706  }
 707  "#);
 708      }
 709  
 710      #[test]
 711      fn single_cyclic_node() {
 712          let labels : Trivial = UnlabelledNodes(1);
 713          let r = test_input(LabelledGraph::new("single_cyclic_node", labels,
 714                                                vec!(Edge(0, 0, "E"))));
 715          assert_eq!(r.unwrap().as_slice(),
 716  r#"digraph single_cyclic_node {
 717      N0[label="N0"];
 718      N0 -> N0[label="E"];
 719  }
 720  "#);
 721      }
 722  
 723      #[test]
 724      fn hasse_diagram() {
 725          let labels = AllNodesLabelled(vec!("{x,y}", "{x}", "{y}", "{}"));
 726          let r = test_input(LabelledGraph::new(
 727              "hasse_diagram", labels,
 728              vec!(Edge(0, 1, ""), Edge(0, 2, ""),
 729                   Edge(1, 3, ""), Edge(2, 3, ""))));
 730          assert_eq!(r.unwrap().as_slice(),
 731  r#"digraph hasse_diagram {
 732      N0[label="{x,y}"];
 733      N1[label="{x}"];
 734      N2[label="{y}"];
 735      N3[label="{}"];
 736      N0 -> N1[label=""];
 737      N0 -> N2[label=""];
 738      N1 -> N3[label=""];
 739      N2 -> N3[label=""];
 740  }
 741  "#);
 742      }
 743  
 744      #[test]
 745      fn left_aligned_text() {
 746          let labels = AllNodesLabelled(vec!(
 747              "if test {\
 748             \\l    branch1\
 749             \\l} else {\
 750             \\l    branch2\
 751             \\l}\
 752             \\lafterward\
 753             \\l",
 754              "branch1",
 755              "branch2",
 756              "afterward"));
 757  
 758          let mut writer = MemWriter::new();
 759  
 760          let g = LabelledGraphWithEscStrs::new(
 761              "syntax_tree", labels,
 762              vec!(Edge(0, 1, "then"), Edge(0, 2, "else"),
 763                   Edge(1, 3, ";"),    Edge(2, 3, ";"   )));
 764  
 765          render(&g, &mut writer).unwrap();
 766          let mut r = BufReader::new(writer.get_ref());
 767          let r = r.read_to_str();
 768  
 769          assert_eq!(r.unwrap().as_slice(),
 770  r#"digraph syntax_tree {
 771      N0[label="if test {\l    branch1\l} else {\l    branch2\l}\lafterward\l"];
 772      N1[label="branch1"];
 773      N2[label="branch2"];
 774      N3[label="afterward"];
 775      N0 -> N1[label="then"];
 776      N0 -> N2[label="else"];
 777      N1 -> N3[label=";"];
 778      N2 -> N3[label=";"];
 779  }
 780  "#);
 781      }
 782  }


libgraphviz/lib.rs:285:53-285:53 -enum- definition:
/// The text for a graphviz label on a node or edge.
pub enum LabelText<'a> {
    /// This kind of label preserves the text directly as is.
references:- 3
404:     /// default is just the output from `node_id`.
405:     fn node_label(&'a self, n: &N) -> LabelText<'a> {
406:         LabelStr(self.node_id(n).name)
--
411:     /// default is in fact the empty string.
412:     fn edge_label(&'a self, e: &E) -> LabelText<'a> {
413:         let _ignored = e;
--
418: impl<'a> LabelText<'a> {
419:     fn escape_char(c: char, f: |char|) {


libgraphviz/lib.rs:338:29-338:29 -struct- definition:
/// `Id` is a Graphviz `ID`.
pub struct Id<'a> {
    name: str::MaybeOwned<'a>,
references:- 5
361:         }
362:         return Id{ name: name };
--
399:     /// is a valid DOT identifier.
400:     fn node_id(&'a self, n: &N) -> Id<'a>;


libgraphviz/lib.rs:480:4-480:4 -fn- definition:
    fn writeln<W:Writer>(w: &mut W, arg: &[&str]) -> io::IoResult<()> {
        for &s in arg.iter() { try!(w.write_str(s)); }
        w.write_char('\n')
references:- 4
509:     writeln(w, ["}"])
510: }


libgraphviz/lib.rs:364:8-364:8 -fn- definition:
        fn is_letter_or_underscore(c: char) -> bool {
            in_range('a', c, 'z') || in_range('A', c, 'Z') || c == '_'
        }
references:- 2
358:             let mut chars = name.as_slice().chars();
359:             assert!(is_letter_or_underscore(chars.next().unwrap()));
360:             assert!(chars.all(is_constituent));
--
367:         fn is_constituent(c: char) -> bool {
368:             is_letter_or_underscore(c) || in_range('0', c, '9')
369:         }


libgraphviz/lib.rs:485:4-485:4 -fn- definition:
    fn indent<W:Writer>(w: &mut W) -> io::IoResult<()> {
        w.write_str("    ")
    }
references:- 2
490:     for n in g.nodes().iter() {
491:         try!(indent(w));
492:         let id = g.node_id(n);
--
499:         let escaped_label = g.edge_label(e).escape();
500:         try!(indent(w));
501:         let source = g.source(e);


libgraphviz/lib.rs:370:8-370:8 -fn- definition:
        fn in_range(low: char, c: char, high: char) -> bool {
            low as uint <= c as uint && c as uint <= high as uint
        }
references:- 3
364:         fn is_letter_or_underscore(c: char) -> bool {
365:             in_range('a', c, 'z') || in_range('A', c, 'Z') || c == '_'
366:         }
367:         fn is_constituent(c: char) -> bool {
368:             is_letter_or_underscore(c) || in_range('0', c, '9')
369:         }