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}