graphviz_rust/lib.rs
1//! The library provides functionality for interacting with the [`graphviz` DOT language].
2//!
3//! # Description:
4//! This library contains 4 primary functions:
5//! - [parse]: parses a string in the dot [`notation`] into a [Graph].
6//! - [print](crate::print): serializes a [Graph] into a string given a [DotPrinter].
7//! - [exec]: executes the [`dot` command line executable] given a [Graph].
8//! - [exec_dot]: executes the [`dot` command line executable] given a string in
9//! the dot [`notation`].
10//!
11//! # Examples:
12//! ```rust
13//! use dot_generator::*;
14//! use dot_structures::*;
15//! use graphviz_rust::{
16//! attributes::*,
17//! cmd::{CommandArg, Format},
18//! exec, exec_dot, parse,
19//! printer::{DotPrinter, PrinterContext},
20//! };
21//!
22//! let g: Graph = parse(
23//! r#"
24//! strict digraph t {
25//! aa[color=green]
26//! subgraph v {
27//! aa[shape=square]
28//! subgraph vv{a2 -> b2}
29//! aaa[color=red]
30//! aaa -> bbb
31//! }
32//! aa -> be -> subgraph v { d -> aaa}
33//! aa -> aaa -> v
34//! }
35//! "#,
36//! )
37//! .unwrap();
38//!
39//! assert_eq!(
40//! g,
41//! graph!(strict di id!("t");
42//! node!("aa";attr!("color","green")),
43//! subgraph!("v";
44//! node!("aa"; attr!("shape","square")),
45//! subgraph!("vv"; edge!(node_id!("a2") => node_id!("b2"))),
46//! node!("aaa";attr!("color","red")),
47//! edge!(node_id!("aaa") => node_id!("bbb"))
48//! ),
49//! edge!(node_id!("aa") => node_id!("be") => subgraph!("v"; edge!(node_id!("d") => node_id!("aaa")))),
50//! edge!(node_id!("aa") => node_id!("aaa") => node_id!("v"))
51//! )
52//! );
53//!
54//! let mut g = graph!(strict di id!("id"));
55//! assert_eq!(
56//! "strict digraph id {\n\n}".to_string(),
57//! g.print(&mut PrinterContext::default())
58//! );
59//!
60//! fn output_test() {
61//! let mut g = graph!(id!("id");
62//! node!("nod"),
63//! subgraph!("sb";
64//! edge!(node_id!("a") => subgraph!(;
65//! node!("n";
66//! NodeAttributes::color(color_name::black), NodeAttributes::shape(shape::egg))
67//! ))
68//! ),
69//! edge!(node_id!("a1") => node_id!(esc "a2"))
70//! );
71//! let graph_svg = exec(
72//! g,
73//! &mut PrinterContext::default(),
74//! vec![Format::Svg.into()],
75//! )
76//! .unwrap();
77//! }
78//! fn output_exec_from_test() {
79//! let mut g = graph!(id!("id");
80//! node!("nod"),
81//! subgraph!("sb";
82//! edge!(node_id!("a") => subgraph!(;
83//! node!("n";
84//! NodeAttributes::color(color_name::black), NodeAttributes::shape(shape::egg))
85//! ))
86//! ),
87//! edge!(node_id!("a1") => node_id!(esc "a2"))
88//! );
89//! let dot = g.print(&mut PrinterContext::default());
90//! println!("{}", dot);
91//! let format = Format::Svg;
92//!
93//! let graph_svg = exec_dot(dot.clone(), vec![format.into()]).unwrap();
94//!
95//! let graph_svg = exec_dot(dot, vec![format.clone().into()]).unwrap();
96//! }
97//! ```
98//!
99//! [`graphviz`]: https://graphviz.org/
100//! [`graphviz` DOT language]: https://graphviz.org/doc/info/lang.html
101//! [`notation`]: https://graphviz.org/doc/info/lang.html
102//! [`dot` command line executable]: https://graphviz.org/doc/info/command.html
103#![allow(non_camel_case_types)]
104#![allow(dead_code)]
105pub extern crate dot_generator;
106pub extern crate dot_structures;
107pub extern crate into_attr;
108pub extern crate into_attr_derive;
109
110use dot_structures::*;
111
112use crate::printer::{DotPrinter, PrinterContext};
113
114pub mod attributes;
115#[cfg(feature = "graphviz-exec")]
116pub mod cmd;
117mod parser;
118pub mod printer;
119
120#[macro_use]
121extern crate pest_derive;
122extern crate pest;
123
124/// Parses a string into a [Graph].
125pub fn parse(dot: &str) -> Result<Graph, String> {
126 parser::parse(dot)
127}
128
129/// Serializes a [Graph] into a string given a [DotPrinter].
130pub fn print(graph: Graph, ctx: &mut PrinterContext) -> String {
131 graph.print(ctx)
132}
133
134#[cfg(feature = "graphviz-exec")]
135use cmd::CommandArg;
136#[cfg(feature = "graphviz-exec")]
137use std::io;
138
139/// Executes the [`dot` command line executable](https://graphviz.org/doc/info/command.html)
140/// using the given [Graph], [PrinterContext] and command line arguments.
141#[cfg(feature = "graphviz-exec")]
142pub fn exec(graph: Graph, ctx: &mut PrinterContext, args: Vec<CommandArg>) -> io::Result<Vec<u8>> {
143 cmd::exec(print(graph, ctx), args)
144}
145
146/// Executes the [`dot` command line executable](https://graphviz.org/doc/info/command.html)
147/// using the given string dot notation, [PrinterContext] and command line arguments.
148#[cfg(feature = "graphviz-exec")]
149pub fn exec_dot(dot_graph: String, args: Vec<CommandArg>) -> io::Result<Vec<u8>> {
150 cmd::exec(dot_graph, args)
151}
152
153#[cfg(test)]
154mod tests {
155
156 use dot_generator::*;
157 use dot_structures::*;
158
159 use crate::{
160 parse,
161 printer::{DotPrinter, PrinterContext},
162 };
163
164 #[test]
165 fn parse_test() {
166 let g: Graph = parse(
167 r#"
168 strict digraph t {
169 aa[color=green]
170 subgraph v {
171 aa[shape=square]
172 subgraph vv{a2 -> b2}
173 aaa[color=red]
174 aaa -> bbb
175 }
176 aa -> be -> subgraph v { d -> aaa}
177 aa -> aaa -> v
178 }
179 "#,
180 )
181 .unwrap();
182
183 assert_eq!(
184 g,
185 graph!(strict di id!("t");
186 node!("aa";attr!("color","green")),
187 subgraph!("v";
188 node!("aa"; attr!("shape","square")),
189 subgraph!("vv"; edge!(node_id!("a2") => node_id!("b2"))),
190 node!("aaa";attr!("color","red")),
191 edge!(node_id!("aaa") => node_id!("bbb"))
192 ),
193 edge!(node_id!("aa") => node_id!("be") => subgraph!("v"; edge!(node_id!("d") => node_id!("aaa")))),
194 edge!(node_id!("aa") => node_id!("aaa") => node_id!("v"))
195 )
196 )
197 }
198
199 #[test]
200 fn print_test() {
201 let mut g = graph!(id!("id"));
202
203 for el in (1..10000).into_iter() {
204 if el % 2 == 0 {
205 g.add_stmt(stmt!(node!(el)))
206 } else {
207 g.add_stmt(stmt!(subgraph!(el)))
208 }
209 }
210
211 assert_eq!(148898, g.print(&mut PrinterContext::default()).len())
212 }
213
214 #[cfg(windows)]
215 const LS: &'static str = "\r\n";
216 #[cfg(not(windows))]
217 const LS: &'static str = "\n";
218
219 #[test]
220 #[cfg(feature = "graphviz-exec")]
221 fn exec_test() {
222 use std::{fs, process::Command};
223
224 use crate::{
225 attributes::{color_name, shape, NodeAttributes},
226 cmd::{CommandArg, Format},
227 exec,
228 };
229
230 let g = graph!(id!("id");
231 node!("nod"),
232 subgraph!("sb";
233 edge!(node_id!("a") => subgraph!(;
234 node!("n";
235 NodeAttributes::color(color_name::black), NodeAttributes::shape(shape::egg))
236 ))
237 ),
238 edge!(node_id!("a1") => node_id!(esc "a2"))
239 );
240 let graph_str = "graph id {\n nod\n subgraph sb {\n a -- subgraph {n[color=black,shape=egg]}\n }\n a1 -- \"a2\"\n}";
241
242 let mut ctx = PrinterContext::default();
243 assert_eq!(graph_str, g.print(&mut ctx));
244
245 let child = Command::new("dot")
246 .arg("-V")
247 .output()
248 .expect("dot command failed to start");
249
250 let output = String::from_utf8_lossy(&child.stderr);
251 let version = output
252 .strip_prefix("dot - ")
253 .and_then(|v| v.strip_suffix(LS))
254 .expect("the version of client is unrecognizable ");
255 println!("{}", version);
256
257 let out_svg = exec(g.clone(), &mut ctx, vec![CommandArg::Format(Format::Svg)]).unwrap();
258
259 let p = "1.svg";
260 let out = exec(
261 g.clone(),
262 &mut PrinterContext::default(),
263 vec![
264 CommandArg::Format(Format::Svg),
265 CommandArg::Output(p.to_string()),
266 ],
267 )
268 .unwrap();
269
270 let file = fs::read_to_string(p).unwrap();
271
272 fs::remove_file(p).unwrap();
273 assert_eq!(Vec::<u8>::new(), out);
274 assert_eq!(out_svg, file.as_bytes());
275 }
276
277 #[test]
278 #[cfg(feature = "graphviz-exec")]
279 fn output_exec_from_test() {
280 use crate::{
281 attributes::{color_name, shape, NodeAttributes},
282 cmd::Format,
283 exec_dot,
284 };
285
286 let g = graph!(id!("id");
287 node!("nod"),
288 subgraph!("sb";
289 edge!(node_id!("a") => subgraph!(;
290 node!("n";
291 NodeAttributes::color(color_name::black), NodeAttributes::shape(shape::egg))
292 ))
293 ),
294 edge!(node_id!("a1") => node_id!(esc "a2"))
295 );
296 let dot = g.print(&mut PrinterContext::default());
297 let format = Format::Svg;
298
299 let res1 = exec_dot(dot.clone(), vec![format.into()]).unwrap();
300 let res2 = exec_dot(dot.clone(), vec![format.clone().into()]).unwrap();
301
302 assert_eq!(res1, res2)
303 }
304}