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(crate) 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(crate) 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