1use dot_structures::{Attribute, Edge, EdgeTy, Graph, GraphAttributes, Id, Node, NodeId, Port, Stmt, Subgraph, Vertex};
16
17pub struct PrinterContext {
29 is_digraph: bool,
31 semi: bool,
33 indent: usize,
35 indent_step: usize,
37 l_s: String,
39 inline_size: usize,
41 l_s_i: String,
42 l_s_m: String,
43}
44
45impl PrinterContext {
46 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 pub fn with_semi(&mut self) -> &mut PrinterContext {
54 self.semi = true;
55 self
56 }
57 pub fn with_indent_step(&mut self, step: usize) -> &mut PrinterContext {
59 self.indent_step = step;
60 self
61 }
62 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 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
123pub 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}