1//! Generating Graphviz `dot` files from our IR.
2
3use super::context::{BindgenContext, ItemId};
4use super::traversal::Trace;
5use std::fs::File;
6use std::io::{self, Write};
7use std::path::Path;
8
9/// A trait for anything that can write attributes as `<table>` rows to a dot
10/// file.
11pub trait DotAttributes {
12    /// Write this thing's attributes to the given output. Each attribute must
13    /// be its own `<tr>...</tr>`.
14    fn dot_attributes<W>(
15        &self,
16        ctx: &BindgenContext,
17        out: &mut W,
18    ) -> io::Result<()>
19    where
20        W: io::Write;
21}
22
23/// Write a graphviz dot file containing our IR.
24pub fn write_dot_file<P>(ctx: &BindgenContext, path: P) -> io::Result<()>
25where
26    P: AsRef<Path>,
27{
28    let file = File::create(path)?;
29    let mut dot_file = io::BufWriter::new(file);
30    writeln!(&mut dot_file, "digraph {{")?;
31
32    let mut err: Option<io::Result<_>> = None;
33
34    for (id, item) in ctx.items() {
35        let is_allowlisted = ctx.allowlisted_items().contains(&id);
36
37        writeln!(
38            &mut dot_file,
39            r#"{} [fontname="courier", color={}, label=< <table border="0" align="left">"#,
40            id.as_usize(),
41            if is_allowlisted { "black" } else { "gray" }
42        )?;
43        item.dot_attributes(ctx, &mut dot_file)?;
44        writeln!(&mut dot_file, r#"</table> >];"#)?;
45
46        item.trace(
47            ctx,
48            &mut |sub_id: ItemId, edge_kind| {
49                if err.is_some() {
50                    return;
51                }
52
53                match writeln!(
54                    &mut dot_file,
55                    "{} -> {} [label={:?}, color={}];",
56                    id.as_usize(),
57                    sub_id.as_usize(),
58                    edge_kind,
59                    if is_allowlisted { "black" } else { "gray" }
60                ) {
61                    Ok(_) => {}
62                    Err(e) => err = Some(Err(e)),
63                }
64            },
65            &(),
66        );
67
68        if let Some(err) = err {
69            return err;
70        }
71
72        if let Some(module) = item.as_module() {
73            for child in module.children() {
74                writeln!(
75                    &mut dot_file,
76                    "{} -> {} [style=dotted, color=gray]",
77                    item.id().as_usize(),
78                    child.as_usize()
79                )?;
80            }
81        }
82    }
83
84    writeln!(&mut dot_file, "}}")?;
85    Ok(())
86}
87