datafusion_common/display/
graphviz.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Logic related to creating DOT language graphs.
19
20use std::fmt;
21
22#[derive(Default)]
23pub struct GraphvizBuilder {
24    id_gen: usize,
25}
26
27impl GraphvizBuilder {
28    // Generate next id in graphviz.
29    pub fn next_id(&mut self) -> usize {
30        self.id_gen += 1;
31        self.id_gen
32    }
33
34    // Write out the start of whole graph.
35    pub fn start_graph(&mut self, f: &mut fmt::Formatter) -> fmt::Result {
36        writeln!(
37            f,
38            r#"
39// Begin DataFusion GraphViz Plan,
40// display it online here: https://dreampuf.github.io/GraphvizOnline
41"#
42        )?;
43        writeln!(f, "digraph {{")
44    }
45
46    pub fn end_graph(&mut self, f: &mut fmt::Formatter) -> fmt::Result {
47        writeln!(f, "}}")?;
48        writeln!(f, "// End DataFusion GraphViz Plan")
49    }
50
51    // write out the start of the subgraph cluster
52    pub fn start_cluster(&mut self, f: &mut fmt::Formatter, title: &str) -> fmt::Result {
53        writeln!(f, "  subgraph cluster_{}", self.next_id())?;
54        writeln!(f, "  {{")?;
55        writeln!(f, "    graph[label={}]", Self::quoted(title))
56    }
57
58    // write out the end of the subgraph cluster
59    pub fn end_cluster(&mut self, f: &mut fmt::Formatter) -> fmt::Result {
60        writeln!(f, "  }}")
61    }
62
63    /// makes a quoted string suitable for inclusion in a graphviz chart
64    pub fn quoted(label: &str) -> String {
65        let label = label.replace('"', "_");
66        format!("\"{label}\"")
67    }
68
69    pub fn add_node(
70        &self,
71        f: &mut fmt::Formatter,
72        id: usize,
73        label: &str,
74        tooltip: Option<&str>,
75    ) -> fmt::Result {
76        if let Some(tooltip) = tooltip {
77            writeln!(
78                f,
79                "    {}[shape=box label={}, tooltip={}]",
80                id,
81                GraphvizBuilder::quoted(label),
82                GraphvizBuilder::quoted(tooltip),
83            )
84        } else {
85            writeln!(
86                f,
87                "    {}[shape=box label={}]",
88                id,
89                GraphvizBuilder::quoted(label),
90            )
91        }
92    }
93
94    pub fn add_edge(
95        &self,
96        f: &mut fmt::Formatter,
97        from_id: usize,
98        to_id: usize,
99    ) -> fmt::Result {
100        writeln!(
101            f,
102            "    {from_id} -> {to_id} [arrowhead=none, arrowtail=normal, dir=back]"
103        )
104    }
105}