1use std::collections::HashMap;
15
16use dot_structures::{
17 Attribute, Edge, EdgeTy, Graph, GraphAttributes, Id, Node, NodeId, Port, Stmt, Subgraph, Vertex,
18};
19
20pub type AttributeValuePrinter = dyn Fn(&str, &str, &str, usize) -> String;
45
46pub struct PrinterContext {
57 is_digraph: bool,
59 semi: bool,
61 mult_node_attr_on_s_l: bool,
63 mult_node_attr_on_s_l_comma: bool,
65 indent: usize,
67 indent_step: usize,
69 l_s: String,
71 inline_size: usize,
73 l_s_i: String,
74 l_s_m: String,
75 attr_value_printers: HashMap<Id, Box<AttributeValuePrinter>>,
77}
78
79impl PrinterContext {
80 pub fn always_inline(&mut self) -> &mut PrinterContext {
82 self.l_s_m = self.l_s_i.clone();
83 self.l_s = self.l_s_i.clone();
84 self
85 }
86 pub fn with_semi(&mut self) -> &mut PrinterContext {
88 self.semi = true;
89 self
90 }
91 pub fn with_node_mult_attr_s_l(&mut self) -> &mut PrinterContext {
93 self.mult_node_attr_on_s_l = true;
94 self
95 }
96 pub fn with_no_node_mult_attr_s_l_comma(&mut self) -> &mut PrinterContext {
98 self.mult_node_attr_on_s_l_comma = false;
99 self
100 }
101 pub fn with_indent_step(&mut self, step: usize) -> &mut PrinterContext {
103 self.indent_step = step;
104 self
105 }
106 pub fn with_line_sep(&mut self, sep: String) -> &mut PrinterContext {
108 self.l_s = sep.clone();
109 self.l_s_m = sep;
110 self
111 }
112 pub fn with_inline_size(&mut self, inline_s: usize) -> &mut PrinterContext {
116 self.inline_size = inline_s;
117 self
118 }
119 pub fn with_attr_value_printer(
121 &mut self,
122 attr_id: Id,
123 fmt: Box<AttributeValuePrinter>,
124 ) -> &mut PrinterContext {
125 self.attr_value_printers.insert(attr_id, fmt);
126 self
127 }
128
129 pub fn new(semi: bool, indent_step: usize, line_s: String, inline_size: usize) -> Self {
130 PrinterContext {
131 is_digraph: false,
132 semi,
133 mult_node_attr_on_s_l: false,
134 mult_node_attr_on_s_l_comma: true,
135 indent: 0,
136 indent_step,
137 inline_size,
138 l_s: line_s.clone(),
139 l_s_i: line_s,
140 l_s_m: "".to_string(),
141 attr_value_printers: HashMap::new(),
142 }
143 }
144}
145
146impl PrinterContext {
147 fn indent(&self) -> String {
148 if self.is_inline_on() {
149 "".to_string()
150 } else {
151 " ".repeat(self.indent)
152 }
153 }
154 fn indent_grow(&mut self) {
155 if !self.is_inline_on() {
156 self.indent += self.indent_step
157 }
158 }
159 fn indent_shrink(&mut self) {
160 if !self.is_inline_on() {
161 self.indent -= self.indent_step
162 }
163 }
164
165 fn is_inline_on(&self) -> bool {
166 self.l_s == self.l_s_i
167 }
168 fn inline_mode(&mut self) {
169 self.l_s = self.l_s_i.clone()
170 }
171 fn multiline_mode(&mut self) {
172 self.l_s = self.l_s_m.clone()
173 }
174}
175
176impl Default for PrinterContext {
177 fn default() -> Self {
178 PrinterContext {
179 is_digraph: false,
180 mult_node_attr_on_s_l: false,
181 mult_node_attr_on_s_l_comma: true,
182 semi: false,
183 indent: 0,
184 indent_step: 2,
185 l_s: "\n".to_string(),
186 inline_size: 90,
187 l_s_i: "".to_string(),
188 l_s_m: "\n".to_string(),
189 attr_value_printers: HashMap::new(),
190 }
191 }
192}
193
194pub trait DotPrinter {
211 fn print(&self, ctx: &mut PrinterContext) -> String;
212}
213
214impl DotPrinter for Id {
215 fn print(&self, _ctx: &mut PrinterContext) -> String {
216 match self {
217 Id::Html(v) | Id::Escaped(v) | Id::Plain(v) => v.clone(),
218 Id::Anonymous(_) => "".to_string(),
219 }
220 }
221}
222
223impl DotPrinter for Port {
224 fn print(&self, ctx: &mut PrinterContext) -> String {
225 match self {
226 Port(Some(id), Some(d)) => format!(":{}:{}", id.print(ctx), d),
227 Port(None, Some(d)) => format!(":{}", d),
228 Port(Some(id), None) => format!(":{}", id.print(ctx)),
229 _ => unreachable!(""),
230 }
231 }
232}
233
234impl DotPrinter for NodeId {
235 fn print(&self, ctx: &mut PrinterContext) -> String {
236 match self {
237 NodeId(id, None) => id.print(ctx),
238 NodeId(id, Some(port)) => [id.print(ctx), port.print(ctx)].join(""),
239 }
240 }
241}
242
243impl DotPrinter for Attribute {
244 fn print(&self, ctx: &mut PrinterContext) -> String {
245 match self {
246 Attribute(l, r) => {
247 let l_val = l.print(ctx);
248 let r_val = r.print(ctx);
249 if let Some(formatter) = ctx.attr_value_printers.get(l) {
250 format!(
251 "{}={}",
252 l_val,
253 formatter(&r_val, &ctx.l_s, &ctx.indent(), ctx.indent_step)
254 )
255 } else {
256 format!("{}={}", l_val, r_val)
257 }
258 }
259 }
260 }
261}
262
263impl DotPrinter for Vec<Attribute> {
264 fn print(&self, ctx: &mut PrinterContext) -> String {
265 let attrs: Vec<String> = self.iter().map(|e| e.print(ctx)).collect();
266 if attrs.is_empty() {
267 "".to_string()
268 } else if attrs.len() > 1 && ctx.mult_node_attr_on_s_l {
269 let indent = ctx.indent();
270 ctx.indent_grow();
271 let r = format!(
272 "[{}{}{}{}{}]",
273 ctx.l_s,
274 ctx.indent(),
275 attrs.join(&format!(
276 "{}{}{}",
277 {
278 if ctx.is_inline_on() || ctx.mult_node_attr_on_s_l_comma {
279 ","
280 } else {
281 ""
282 }
283 },
284 ctx.l_s,
285 ctx.indent()
286 )),
287 ctx.l_s,
288 indent,
289 );
290 ctx.indent_shrink();
291 r
292 } else {
293 format!("[{}]", attrs.join(","))
294 }
295 }
296}
297
298impl DotPrinter for GraphAttributes {
299 fn print(&self, ctx: &mut PrinterContext) -> String {
300 match self {
301 GraphAttributes::Graph(attrs) => format!("graph{}", attrs.print(ctx)),
302 GraphAttributes::Node(attrs) => format!("node{}", attrs.print(ctx)),
303 GraphAttributes::Edge(attrs) => format!("edge{}", attrs.print(ctx)),
304 }
305 }
306}
307
308impl DotPrinter for Node {
309 fn print(&self, ctx: &mut PrinterContext) -> String {
310 format!("{}{}", self.id.print(ctx), self.attributes.print(ctx))
311 }
312}
313
314impl DotPrinter for Vertex {
315 fn print(&self, ctx: &mut PrinterContext) -> String {
316 match self {
317 Vertex::N(el) => el.print(ctx),
318 Vertex::S(el) => el.print(ctx),
319 }
320 }
321}
322
323impl DotPrinter for Subgraph {
324 fn print(&self, ctx: &mut PrinterContext) -> String {
325 let indent = ctx.indent();
326 ctx.indent_grow();
327 let header = format!("subgraph {} {{{}", self.id.print(ctx), ctx.l_s);
328 let r = format!("{}{}{}{}}}", header, self.stmts.print(ctx), ctx.l_s, indent);
329 ctx.indent_shrink();
330 r
331 }
332}
333
334impl DotPrinter for Graph {
335 fn print(&self, ctx: &mut PrinterContext) -> String {
336 ctx.indent_grow();
337
338 match self {
339 Graph::Graph { id, strict, stmts } if *strict => {
340 ctx.is_digraph = false;
341 let body = stmts.print(ctx);
342 format!(
343 "strict graph {} {{{}{}{}}}",
344 id.print(ctx),
345 ctx.l_s,
346 body,
347 ctx.l_s
348 )
349 }
350 Graph::Graph {
351 id,
352 strict: _,
353 stmts,
354 } => {
355 ctx.is_digraph = false;
356 let body = stmts.print(ctx);
357 format!("graph {} {{{}{}{}}}", id.print(ctx), ctx.l_s, body, ctx.l_s)
358 }
359 Graph::DiGraph { id, strict, stmts } if *strict => {
360 ctx.is_digraph = true;
361 let body = stmts.print(ctx);
362 format!(
363 "strict digraph {} {{{}{}{}}}",
364 id.print(ctx),
365 ctx.l_s,
366 body,
367 ctx.l_s
368 )
369 }
370 Graph::DiGraph {
371 id,
372 strict: _,
373 stmts,
374 } => {
375 ctx.is_digraph = true;
376 let body = stmts.print(ctx);
377 format!(
378 "digraph {} {{{}{}{}}}",
379 id.print(ctx),
380 ctx.l_s,
381 body,
382 ctx.l_s
383 )
384 }
385 }
386 }
387}
388
389impl DotPrinter for Vec<Stmt> {
390 fn print(&self, ctx: &mut PrinterContext) -> String {
391 let attrs: Vec<String> = self.iter().map(|e| e.print(ctx)).collect();
392 attrs.join(ctx.l_s.as_str())
393 }
394}
395
396impl DotPrinter for Stmt {
397 fn print(&self, ctx: &mut PrinterContext) -> String {
398 let end = if ctx.semi { ";" } else { "" };
399 let indent = ctx.indent();
400 match self {
401 Stmt::Node(e) => format!("{}{}{}", indent, e.print(ctx), end),
402 Stmt::Subgraph(e) => format!("{}{}{}", indent, e.print(ctx), end),
403 Stmt::Attribute(e) => format!("{}{}{}", indent, e.print(ctx), end),
404 Stmt::GAttribute(e) => format!("{}{}{}", indent, e.print(ctx), end),
405 Stmt::Edge(e) => format!("{}{}{}", indent, e.print(ctx), end),
406 }
407 }
408}
409
410fn print_edge(edge: &Edge, ctx: &mut PrinterContext) -> String {
411 let bond = if ctx.is_digraph { "->" } else { "--" };
412 match edge {
413 Edge {
414 ty: EdgeTy::Pair(l, r),
415 attributes,
416 } => {
417 if attributes.is_empty() {
418 format!("{} {} {}", l.print(ctx), bond, r.print(ctx))
419 } else {
420 format!(
421 "{} {} {} {}",
422 l.print(ctx),
423 bond,
424 r.print(ctx),
425 attributes.print(ctx)
426 )
427 }
428 }
429 Edge {
430 ty: EdgeTy::Chain(vs),
431 attributes,
432 } => {
433 let mut iter = vs.iter();
434 let h = iter.next().unwrap().print(ctx);
435 let mut chain = h;
436 for el in iter {
437 chain = format!("{} {} {}", chain, bond, el.print(ctx))
438 }
439 format!("{}{}", chain, attributes.print(ctx))
440 }
441 }
442}
443
444impl DotPrinter for Edge {
445 fn print(&self, ctx: &mut PrinterContext) -> String {
446 let mut edge_str = print_edge(self, ctx);
447 if edge_str.len() <= ctx.inline_size && !ctx.is_inline_on() {
448 ctx.inline_mode();
449 edge_str = print_edge(self, ctx);
450 ctx.multiline_mode();
451 }
452
453 edge_str
454 }
455}
456
457#[cfg(test)]
458mod tests {
459 use dot_generator::{attr, edge, graph, id, node, node_id, port, stmt, subgraph};
460 use dot_structures::*;
461
462 use crate::printer::{DotPrinter, PrinterContext};
463
464 #[test]
465 fn edge_test() {
466 let mut ctx = PrinterContext::default();
467 let edge = edge!(node_id!("abc") => node_id!("bce") => node_id!("cde"); attr!("a",2));
468 assert_eq!(edge.print(&mut ctx), "abc -- bce -- cde[a=2]");
469 ctx.is_digraph = true;
470 assert_eq!(edge.print(&mut ctx), "abc -> bce -> cde[a=2]");
471 }
472
473 #[test]
474 fn node_id_test() {
475 let node_id = NodeId(id!("abc"), Some(port!(id!("abc"), "n")));
476 let mut ctx = PrinterContext::default();
477 assert_eq!(node_id.print(&mut ctx), "abc:abc:n".to_string());
478 }
479
480 #[test]
481 fn node_test() {
482 let mut ctx = PrinterContext::default();
483 assert_eq!(
484 node!("abc";attr!("a",2)).print(&mut ctx),
485 "abc[a=2]".to_string()
486 );
487 }
488
489 #[test]
490 fn attr_test() {
491 let mut ctx = PrinterContext::default();
492 let attr = attr!("a", 2);
493 assert_eq!(attr.print(&mut ctx), "a=2".to_string());
494 }
495
496 #[test]
497 fn graph_attr_test() {
498 let mut ctx = PrinterContext::default();
499 let n_attr = GraphAttributes::Node(vec![attr!("a", 2), attr!("b", 3)]);
500 assert_eq!(n_attr.print(&mut ctx), "node[a=2,b=3]".to_string());
501 }
502
503 #[test]
504 fn subgraph_test() {
505 let mut ctx = PrinterContext::default();
506 let s = subgraph!("id"; node!("abc"), edge!(node_id!("a") => node_id!("b")));
507 println!("{}", s.print(&mut ctx));
508 assert_eq!(
509 s.print(&mut ctx),
510 "subgraph id {\n abc\n a -- b\n}".to_string()
511 );
512 }
513
514 #[test]
515 fn graph_test() {
516 let mut ctx = PrinterContext::default();
517 ctx.always_inline();
518 let g = graph!(strict di id!("t");
519 node!("aa";attr!("color","green")),
520 subgraph!("v";
521 node!("aa"; attr!("shape","square")),
522 subgraph!("vv"; edge!(node_id!("a2") => node_id!("b2"))),
523 node!("aaa";attr!("color","red")),
524 edge!(node_id!("aaa") => node_id!("bbb"))
525 ),
526 edge!(node_id!("aa") => node_id!("be") => subgraph!("v"; edge!(node_id!("d") => node_id!("aaa")))),
527 edge!(node_id!("aa") => node_id!("aaa") => node_id!("v"))
528 );
529 assert_eq!(
530 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}"#,
531 g.print(&mut ctx)
532 );
533 }
534
535 #[test]
536 fn semi_graph_test() {
537 let mut ctx = PrinterContext::default();
538 let g = graph!(strict di id!("t");
539 node!("aa";attr!("color","green")),
540 subgraph!("v";
541 node!("aa"; attr!("shape","square")),
542 subgraph!("vv"; edge!(node_id!("a2") => node_id!("b2"))),
543 node!("aaa";attr!("color","red")),
544 edge!(node_id!("aaa") => node_id!("bbb"))
545 ),
546 edge!(node_id!("aa") => node_id!("be") => subgraph!("v"; edge!(node_id!("d") => node_id!("aaa")))),
547 edge!(node_id!("aa") => node_id!("aaa") => node_id!("v"))
548 );
549 assert_eq!(
550 "strict digraph t {\n aa[color=green];\n subgraph v {\n aa[shape=square];\n subgraph vv {\n a2 -> b2;\n };\n aaa[color=red];\n aaa -> bbb;\n };\n aa -> be -> subgraph v {d -> aaa;};\n aa -> aaa -> v;\n}",
551 g.print(ctx.with_semi())
552 );
553 }
554
555 #[test]
556 fn indent_step_graph_test() {
557 let mut ctx = PrinterContext::default();
558 let g = graph!(strict di id!("t");
559 node!("aa";attr!("color","green")),
560 subgraph!("v";
561 node!("aa"; attr!("shape","square")),
562 subgraph!("vv"; edge!(node_id!("a2") => node_id!("b2"))),
563 node!("aaa";attr!("color","red")),
564 edge!(node_id!("aaa") => node_id!("bbb"))
565 ),
566 edge!(node_id!("aa") => node_id!("be") => subgraph!("v"; edge!(node_id!("d") => node_id!("aaa")))),
567 edge!(node_id!("aa") => node_id!("aaa") => node_id!("v"))
568 );
569 assert_eq!(
570 "strict digraph t {\n aa[color=green]\n subgraph v {\n aa[shape=square]\n subgraph vv {\n a2 -> b2\n }\n aaa[color=red]\n aaa -> bbb\n }\n aa -> be -> subgraph v {d -> aaa}\n aa -> aaa -> v\n}",
571 g.print(ctx.with_indent_step(4))
572 );
573 }
574
575 #[test]
576 fn mult_attr_l_s_graph_test() {
577 let mut ctx = PrinterContext::default();
578 let g = graph!(di id!("multi");
579 node!("a";attr!("shape","square")),
580 node!("aa";attr!("color","blue"),attr!("shape","Mrecord")),
581 subgraph!("v";
582 node!("aaa"; attr!("shape","square")),
583 node!("aaaa";attr!("color","red"),attr!("shape","Mrecord")),
584 edge!(node_id!("aaa") => node_id!("aaaa");attr!("label","FALSE"))
585 ),
586 edge!(node_id!("a") => node_id!("aa");attr!("label","TRUE"), attr!("color","green"))
587 );
588 assert_eq!(
589 "digraph multi {\n a[shape=square]\n aa[\n color=blue,\n shape=Mrecord\n ]\n subgraph v {\n aaa[shape=square]\n aaaa[\n color=red,\n shape=Mrecord\n ]\n aaa -> aaaa [label=FALSE]\n }\n a -> aa [label=TRUE,color=green]\n}",
590 g.print(ctx.with_node_mult_attr_s_l())
591 );
592 }
593
594 #[test]
595 fn mult_attr_l_s_graph_test_no_comma() {
596 let mut ctx = PrinterContext::default();
597 let g = graph!(di id!("multi");
598 node!("a";attr!("shape","square")),
599 node!("aa";attr!("color","blue"),attr!("shape","Mrecord")),
600 subgraph!("v";
601 node!("aaa"; attr!("shape","square")),
602 node!("aaaa";attr!("color","red"),attr!("shape","Mrecord")),
603 edge!(node_id!("aaa") => node_id!("aaaa");attr!("label","FALSE"))
604 ),
605 edge!(node_id!("a") => node_id!("aa");attr!("label","TRUE"), attr!("color","green"))
606 );
607 assert_eq!(
608 "digraph multi {\n a[shape=square]\n aa[\n color=blue\n shape=Mrecord\n ]\n subgraph v {\n aaa[shape=square]\n aaaa[\n color=red\n shape=Mrecord\n ]\n aaa -> aaaa [label=FALSE]\n }\n a -> aa [label=TRUE,color=green]\n}",
609 g.print(ctx.with_node_mult_attr_s_l().with_no_node_mult_attr_s_l_comma())
610 );
611 }
612
613 #[test]
614 fn mult_attr_l_s_graph_test_no_comma_no_inline() {
615 let mut ctx = PrinterContext::default();
616 let g = graph!(di id!("multi");
617 node!("a";attr!("shape","square")),
618 node!("aa";attr!("color","blue"),attr!("shape","Mrecord")),
619 subgraph!("v";
620 node!("aaa"; attr!("shape","square")),
621 node!("aaaa";attr!("color","red"),attr!("shape","Mrecord")),
622 edge!(node_id!("aaa") => node_id!("aaaa");attr!("label","FALSE"))
623 ),
624 edge!(node_id!("a") => node_id!("aa");attr!("label","TRUE"), attr!("color","green"))
625 );
626 assert_eq!(
627 "digraph multi {\n a[shape=square]\n aa[\n color=blue\n shape=Mrecord\n ]\n subgraph v {\n aaa[shape=square]\n aaaa[\n color=red\n shape=Mrecord\n ]\n aaa -> aaaa [label=FALSE]\n }\n a -> aa [\n label=TRUE\n color=green\n ]\n}",
628 g.print(ctx.with_node_mult_attr_s_l().with_no_node_mult_attr_s_l_comma().with_inline_size(0))
629 );
630 }
631
632 #[test]
633 fn attr_formatter_graph_test() {
634 let mut ctx = PrinterContext::default();
635 let g = graph!(di id!("multi");
636 node!("a";attr!("shape","square")),
637 node!("aa";attr!("color","blue"),attr!("custom", esc "Custom Text"),attr!("shape","Mrecord")),
638 subgraph!("v";
639 node!("aaa"; attr!("shape","square")),
640 node!("aaaa";attr!("color","red"),attr!("custom", esc "Custom Text2")),
641 edge!(node_id!("aaa") => node_id!("aaaa");attr!("label","FALSE"))
642 ),
643 edge!(node_id!("a") => node_id!("aa");attr!("label","TRUE"), attr!("color","green"))
644 );
645 assert_eq!(
646 "digraph multi {\n a[shape=square]\n aa[\n color=blue,\n custom=\"**Custom Text**\",\n shape=Mrecord\n ]\n subgraph v {\n aaa[shape=square]\n aaaa[\n color=red,\n custom=\"**Custom Text2**\"\n ]\n aaa -> aaaa [label=FALSE]\n }\n a -> aa [label=TRUE,color=green]\n}",
647 g.print(ctx.with_node_mult_attr_s_l().with_attr_value_printer(id!("custom"), Box::new(|value, _l_s, _indent, _i_s| {
648 format!(r#""**{}**""#, value.trim_matches('"'))
649 })))
650 );
651 }
652}