1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! # Graphviz Writer
//!
//! This library is an ergonomic library for plotting graphs. It outputs the [Graphviz](https://www.graphviz.org/)
//! language [DOT](https://www.graphviz.org/doc/info/lang.html). Graphs written in DOT can then be easily
//! converted to SVG or other image formats using the Graphviz [dot executable](https://graphviz.org/doc/info/command.html).
//!
//! The structs in this library leverage the Rust type system and lifetimes to ensure that it's hard
//! to use them to construct an invalid DOT graph. It's important to note that this means you need
//! to make sure that child structs go out of scope before using their parents again. This is to make sure that
//! the [`Drop`] writes the closing brackets correctly. The compiler will tell you if you forget to do this though.
//!
//! ## Non Goals
//!
//! This library only writes DOT in a strongly typed way. It doesn't read DOT or render DOT into image files.
//!
//! ## Usage
//!
//! The first part of the library is the [`DotWriter`] struct, which is constructed from a mutable reference to any struct
//! that implements [`std::io::Write`] (for example [`std::io::stdout()`] or even just a [`Vec<u8>`]).
//! This [`DotWriter`] can then be used to construct a graph:
//!
//! ```
//! use dot_writer::{Color, DotWriter};
//!
//! let mut output_bytes = Vec::new();
//! {
//!     let mut writer = DotWriter::from(&mut output_bytes);
//!     writer.set_pretty_print(false);
//!     writer
//!         .digraph()
//!         .edge("Hello", "World"); // digraph goes out of scope here, writing closing bracket
//!     // writer goes out of scope here, freeing up output_bytes for reading
//! }
//! assert_eq!(
//!     String::from_utf8(output_bytes).unwrap(),
//!     "digraph{Hello->World;}"
//! );
//! ```
//!
//! If instead you used [`std::io::stdout()`] or wrote to a file using
//! [File or BufReader](https://doc.rust-lang.org/std/fs/struct.File.html),
//! you could then use the [dot executable](https://graphviz.org/doc/info/command.html) to create an image file:
//!
//! ```bash
//! $ echo "digraph {Hello->World;}" | dot -Tsvg > mygraph.svg
//! ```
//!
//! This generate the following image (you can open in firefox or chrome, or switch the image type to `png`
//! and use an image viewer of your choice):
//!
//! ![Hello World example graph](https://graphviz.org/Gallery/directed/hello.svg)
//!
//! Heres a more complex example, demonstrating clustering, colors, labeling and shapes:
//!
//! ```
//! use dot_writer::{Color, DotWriter, Attributes, Shape, Style};
//!
//! let mut output_bytes = Vec::new();
//! {
//!     let mut writer = DotWriter::from(&mut output_bytes);
//!     writer.set_pretty_print(false);
//!     let mut digraph = writer.digraph();
//!     {
//!         let mut cluster = digraph.cluster();
//!         cluster.set_style(Style::Filled);
//!         cluster.set_color(Color::LightGrey);
//!         cluster.node_attributes()
//!             .set_style(Style::Filled)
//!             .set_color(Color::White);
//!         cluster.edge("a0", "a1").edge("a2").edge("a3");
//!         cluster.set_label("process #1");
//!         // cluster goes out of scope here to write closing bracket
//!     }
//!     {
//!         let mut cluster = digraph.cluster();
//!         cluster.node_attributes()
//!             .set_style(Style::Filled);
//!         cluster.edge("b0", "b1").edge("b2").edge("b3");
//!         cluster.set_label("process #2");
//!         cluster.set_color(Color::Blue);
//!         // cluster goes out of scope here to write closing bracket
//!     }
//!     digraph.edge("start", "a0");
//!     digraph.edge("start", "b0");
//!     digraph.edge("a1", "b3");
//!     digraph.edge("b2", "a3");
//!     digraph.edge("a3", "a0");
//!     digraph.edge("a3", "end");
//!     digraph.edge("b3", "end");
//!     digraph.node_named("start")
//!         .set_shape(Shape::Mdiamond);
//!     digraph.node_named("end")
//!         .set_shape(Shape::Msquare);
//!     // digraph goes out of scope here to write closing bracket
//!     // then writer goes out of scope here to free up output_bytes for reading
//! }
//! assert_eq!(
//!     String::from_utf8(output_bytes).unwrap(),
//!     "digraph{subgraph cluster_0{style=\"filled\";color=lightgray;node[style=\"filled\",color=white];a0->a1->a2->a3;label=\"process #1\";}subgraph cluster_1{node[style=\"filled\"];b0->b1->b2->b3;label=\"process #2\";color=blue;}start->a0;start->b0;a1->b3;b2->a3;a3->a0;a3->end;b3->end;start[shape=Mdiamond];end[shape=Msquare];}"
//! );
//! ```
//!
//! This produces (after render with dot) the following lovely graph:
//!
//! ![More complex example graph](https://graphviz.org/Gallery/directed/cluster.svg)

#[allow(clippy::cargo, clippy::pedantic, clippy::nursery)]
mod attribute;
mod scope;
mod writer;

pub use attribute::{
    ArrowType, Attributes, AttributesList, Color, Rank, RankDirection, Shape, Style,
};
pub use scope::{EdgeList, Node, NodeId, PortId, PortPosId, PortPosition, Scope};
pub use writer::DotWriter;