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