graphviz_rust_bla/
printer.rs

1//! It allows to transform a graph into a string carrying dot info according
2//! to the notation.
3//!
4//! # Example:
5//! ```rust
6//!     use dot_generator::*;
7//!     use dot_structures::*;
8//!     use graphviz_rust::printer::{PrinterContext,DotPrinter};
9//!     fn subgraph_test() {
10//!         let mut ctx = PrinterContext::default();
11//!         let s = subgraph!("id"; node!("abc"), edge!(node_id!("a") => node_id!("b")));
12//!         assert_eq!(s.print(&mut ctx), "subgraph id {\n    abc\n    a -- b \n}".to_string());
13//!     }
14//! ```
15use dot_structures::{Attribute, Edge, EdgeTy, Graph, GraphAttributes, Id, Node, NodeId, Port, Stmt, Subgraph, Vertex};
16
17/// Context allows to customize the output of the file.
18/// # Example:
19/// ```rust
20///     fn ctx(){
21///         use self::graphviz_rust::printer::PrinterContext;
22///
23///         let mut ctx =PrinterContext::default();
24///         ctx.always_inline();
25///         ctx.with_indent_step(4);
26///     }
27/// ```
28pub struct PrinterContext {
29    /// internal flag which is decoupled from the graph
30    is_digraph: bool,
31    /// a flag adds a semicolon at the end of the line
32    semi: bool,
33    /// an initial indent. 0 by default
34    indent: usize,
35    /// a step of the indent. 2 by default
36    indent_step: usize,
37    /// a line separator. can be empty
38    l_s: String,
39    /// a len of the text to keep on one line
40    inline_size: usize,
41    l_s_i: String,
42    l_s_m: String,
43}
44
45impl PrinterContext {
46    /// everything in one line
47    pub fn always_inline(&mut self) -> &mut PrinterContext {
48        self.l_s_m = self.l_s_i.clone();
49        self.l_s = self.l_s_i.clone();
50        self
51    }
52    /// add semi at the end of every line
53    pub fn with_semi(&mut self) -> &mut PrinterContext {
54        self.semi = true;
55        self
56    }
57    /// set a step of the indent
58    pub fn with_indent_step(&mut self, step: usize) -> &mut PrinterContext {
59        self.indent_step = step;
60        self
61    }
62    /// set a specific line sep
63    pub fn with_line_sep(&mut self, sep: String) -> &mut PrinterContext {
64        self.l_s = sep.clone();
65        self.l_s_m = sep.clone();
66        self
67    }
68    /// set a line len enough to fit in a line
69    pub fn with_inline_size(&mut self, inline_s: usize) -> &mut PrinterContext {
70        self.inline_size = inline_s;
71        self
72    }
73
74    pub fn new(semi: bool, indent_step: usize, line_s: String, inline_size: usize) -> Self {
75        PrinterContext {
76            is_digraph: false,
77            semi,
78            indent: 0,
79            indent_step,
80            inline_size,
81            l_s: line_s.clone(),
82            l_s_i: line_s.clone(),
83            l_s_m: "".to_string(),
84        }
85    }
86}
87
88impl PrinterContext {
89    fn indent(&self) -> String {
90        if self.is_inline_on() { "".to_string() } else { " ".repeat(self.indent) }
91    }
92    fn indent_grow(&mut self) {
93        if !self.is_inline_on() { self.indent += self.indent_step }
94    }
95    fn indent_shrink(&mut self) {
96        if !self.is_inline_on() { self.indent -= self.indent_step }
97    }
98
99    fn is_inline_on(&self) -> bool { self.l_s == self.l_s_i }
100    fn inline_mode(&mut self) {
101        self.l_s = self.l_s_i.clone()
102    }
103    fn multiline_mode(&mut self) {
104        self.l_s = self.l_s_m.clone()
105    }
106}
107
108impl Default for PrinterContext {
109    fn default() -> Self {
110        PrinterContext {
111            is_digraph: false,
112            semi: false,
113            indent: 0,
114            indent_step: 2,
115            l_s: "\n".to_string(),
116            inline_size: 90,
117            l_s_i: "".to_string(),
118            l_s_m: "\n".to_string(),
119        }
120    }
121}
122
123/// The trait allowing to transform a graph into the dot file:
124/// # Example:
125///  ```rust
126///     fn test(){
127///         use dot_generator::*;
128///         use dot_structures::*;
129///         use self::graphviz_rust::printer::PrinterContext;
130///         use self::graphviz_rust::printer::DotPrinter;
131///
132///         let mut ctx =PrinterContext::default();
133///         ctx.always_inline();
134///         ctx.with_indent_step(4);
135///         let graph = graph!(strict di id!("t"));
136///
137///         let string = graph.print(&mut ctx);
138///     }
139/// ```
140pub trait DotPrinter {
141    fn print(&self, ctx: &mut PrinterContext) -> String;
142}
143
144impl DotPrinter for Id {
145    fn print(&self, _ctx: &mut PrinterContext) -> String {
146        match self {
147            Id::Html(v) | Id::Escaped(v) | Id::Plain(v) => v.clone(),
148            Id::Anonymous(_) => "".to_string(),
149        }
150    }
151}
152
153impl DotPrinter for Port {
154    fn print(&self, ctx: &mut PrinterContext) -> String {
155        match self {
156            Port(Some(id), Some(d)) => format!(":{}:{}", id.print(ctx), d),
157            Port(None, Some(d)) => format!(":{}", d),
158            Port(Some(id), None) => format!(":{}", id.print(ctx)),
159            _ => unreachable!("")
160        }
161    }
162}
163
164impl DotPrinter for NodeId {
165    fn print(&self, ctx: &mut PrinterContext) -> String {
166        match self {
167            NodeId(id, None) => id.print(ctx),
168            NodeId(id, Some(port)) => [id.print(ctx), port.print(ctx)].join("")
169        }
170    }
171}
172
173impl DotPrinter for Attribute {
174    fn print(&self, ctx: &mut PrinterContext) -> String {
175        match self {
176            Attribute(l, r) => format!("{}={}", l.print(ctx), r.print(ctx))
177        }
178    }
179}
180
181impl DotPrinter for Vec<Attribute> {
182    fn print(&self, ctx: &mut PrinterContext) -> String {
183        let attrs: Vec<String> = self.iter().map(|e| e.print(ctx)).collect();
184        if attrs.is_empty() { "".to_string() } else {
185            format!("[{}]", attrs.join(","))
186        }
187    }
188}
189
190impl DotPrinter for GraphAttributes {
191    fn print(&self, ctx: &mut PrinterContext) -> String {
192        match self {
193            GraphAttributes::Graph(attrs) => format!("graph{}", attrs.print(ctx)),
194            GraphAttributes::Node(attrs) => format!("node{}", attrs.print(ctx)),
195            GraphAttributes::Edge(attrs) => format!("edge{}", attrs.print(ctx)),
196        }
197    }
198}
199
200impl DotPrinter for Node {
201    fn print(&self, ctx: &mut PrinterContext) -> String {
202        format!("{}{}", self.id.print(ctx), self.attributes.print(ctx))
203    }
204}
205
206impl DotPrinter for Vertex {
207    fn print(&self, ctx: &mut PrinterContext) -> String {
208        match self {
209            Vertex::N(el) => el.print(ctx),
210            Vertex::S(el) => el.print(ctx),
211        }
212    }
213}
214
215impl DotPrinter for Subgraph {
216    fn print(&self, ctx: &mut PrinterContext) -> String {
217        let indent = ctx.indent();
218        ctx.indent_grow();
219        let header = format!("subgraph {} {{{}", self.id.print(ctx), ctx.l_s);
220        let r = format!("{}{}{}{}}}", header, self.stmts.print(ctx), ctx.l_s, indent);
221        ctx.indent_shrink();
222        r
223    }
224}
225
226impl DotPrinter for Graph {
227    fn print(&self, ctx: &mut PrinterContext) -> String {
228        ctx.indent_grow();
229
230        match self {
231            Graph::Graph { id, strict, stmts } if *strict => {
232                ctx.is_digraph = false;
233                let body = stmts.print(ctx);
234                format!("strict graph {} {{{}{}{}}}", id.print(ctx), ctx.l_s, body, ctx.l_s)
235            }
236            Graph::Graph { id, strict, stmts } => {
237                ctx.is_digraph = false;
238                let body = stmts.print(ctx);
239                format!("graph {} {{{}{}{}}}", id.print(ctx), ctx.l_s, body, ctx.l_s)
240            }
241            Graph::DiGraph { id, strict, stmts } if *strict => {
242                ctx.is_digraph = true;
243                let body = stmts.print(ctx);
244                format!("strict digraph {} {{{}{}{}}}", id.print(ctx), ctx.l_s, body, ctx.l_s)
245            }
246            Graph::DiGraph { id, strict, stmts } => {
247                ctx.is_digraph = true;
248                let body = stmts.print(ctx);
249                format!("digraph {} {{{}{}{}}}", id.print(ctx), ctx.l_s, body, ctx.l_s)
250            }
251        }
252    }
253}
254
255impl DotPrinter for Vec<Stmt> {
256    fn print(&self, ctx: &mut PrinterContext) -> String {
257        ctx.indent_grow();
258        let attrs: Vec<String> = self.iter().map(|e| e.print(ctx)).collect();
259        ctx.indent_shrink();
260        attrs.join(ctx.l_s.as_str())
261    }
262}
263
264impl DotPrinter for Stmt {
265    fn print(&self, ctx: &mut PrinterContext) -> String {
266        let end = if ctx.semi { ";" } else { "" };
267        let indent = ctx.indent();
268        match self {
269            Stmt::Node(e) => format!("{}{}{}", indent, e.print(ctx), end),
270            Stmt::Subgraph(e) => format!("{}{}{}", indent, e.print(ctx), end),
271            Stmt::Attribute(e) => format!("{}{}{}", indent, e.print(ctx), end),
272            Stmt::GAttribute(e) => format!("{}{}{}", indent, e.print(ctx), end),
273            Stmt::Edge(e) => format!("{}{}{}", indent, e.print(ctx), end),
274        }
275    }
276}
277
278fn print_edge(edge: &Edge, ctx: &mut PrinterContext) -> String {
279    let bond = if ctx.is_digraph { "->" } else { "--" };
280    match edge {
281        Edge { ty: EdgeTy::Pair(l, r), attributes } => {
282            format!("{} {} {} {}", l.print(ctx), bond, r.print(ctx), attributes.print(ctx))
283        }
284        Edge { ty: EdgeTy::Chain(vs), attributes } => {
285            let mut iter = vs.into_iter();
286            let h = iter.next().unwrap().print(ctx);
287            let mut chain = format!("{}", h);
288            for el in iter {
289                chain = format!("{} {} {}", chain, bond, el.print(ctx))
290            }
291            format!("{}{}", chain, attributes.print(ctx))
292        }
293    }
294}
295
296impl DotPrinter for Edge {
297    fn print(&self, ctx: &mut PrinterContext) -> String {
298        let mut edge_str = print_edge(self, ctx);
299        if edge_str.len() <= ctx.inline_size && !ctx.is_inline_on() {
300            ctx.inline_mode();
301            edge_str = print_edge(self, ctx);
302            ctx.multiline_mode();
303        }
304
305        edge_str
306    }
307}
308
309#[cfg(test)]
310mod tests {
311    use dot_generator::{id, port, attr, node, stmt, subgraph, graph, edge, node_id};
312    use dot_structures::*;
313    use crate::printer::{DotPrinter, PrinterContext};
314
315    #[test]
316    fn edge_test() {
317        let mut ctx = PrinterContext::default();
318        let edge = edge!(node_id!("abc") => node_id!("bce") => node_id!("cde"); attr!("a",2));
319        assert_eq!(edge.print(&mut ctx), "abc -- bce -- cde[a=2]");
320        ctx.is_digraph = true;
321        assert_eq!(edge.print(&mut ctx), "abc -> bce -> cde[a=2]");
322    }
323
324    #[test]
325    fn node_id_test() {
326        let node_id = NodeId(id!("abc"), Some(port!( id!("abc"), "n" )));
327        let mut ctx = PrinterContext::default();
328        assert_eq!(node_id.print(&mut ctx), "abc:abc:n".to_string());
329    }
330
331    #[test]
332    fn node_test() {
333        let mut ctx = PrinterContext::default();
334        assert_eq!(node!("abc";attr!("a",2)).print(&mut ctx), "abc[a=2]".to_string());
335    }
336
337    #[test]
338    fn attr_test() {
339        let mut ctx = PrinterContext::default();
340        let attr = attr!("a",2);
341        assert_eq!(attr.print(&mut ctx), "a=2".to_string());
342    }
343
344    #[test]
345    fn graph_attr_test() {
346        let mut ctx = PrinterContext::default();
347        let n_attr = GraphAttributes::Node(vec![attr!("a",2), attr!("b",3)]);
348        assert_eq!(n_attr.print(&mut ctx), "node[a=2,b=3]".to_string());
349    }
350
351    #[test]
352    fn subgraph_test() {
353        let mut ctx = PrinterContext::default();
354        let s = subgraph!("id"; node!("abc"), edge!(node_id!("a") => node_id!("b")));
355        println!("{}", s.print(&mut ctx));
356        assert_eq!(s.print(&mut ctx), "subgraph id {\n    abc\n    a -- b \n}".to_string());
357    }
358
359    #[test]
360    fn graph_test() {
361        let mut ctx = PrinterContext::default();
362        ctx.always_inline();
363        let g = graph!(strict di id!("t");
364              node!("aa";attr!("color","green")),
365              subgraph!("v";
366                node!("aa"; attr!("shape","square")),
367                subgraph!("vv"; edge!(node_id!("a2") => node_id!("b2"))),
368                node!("aaa";attr!("color","red")),
369                edge!(node_id!("aaa") => node_id!("bbb"))
370                ),
371              edge!(node_id!("aa") => node_id!("be") => subgraph!("v"; edge!(node_id!("d") => node_id!("aaa")))),
372              edge!(node_id!("aa") => node_id!("aaa") => node_id!("v"))
373            );
374        assert_eq!(r#"strict digraph t {aa[color=green]subgraph v {aa[shape=square]subgraph vv {a2 -> b2 }aaa[color=red]aaa -> bbb }aa -> be -> subgraph v {d -> aaa }aa -> aaa -> v}"#, g.print(&mut ctx));
375    }
376}