1 //! Generating Graphviz `dot` files from our IR.
2 
3 use super::context::{BindgenContext, ItemId};
4 use super::traversal::Trace;
5 use std::fs::File;
6 use std::io::{self, Write};
7 use std::path::Path;
8 
9 /// A trait for anything that can write attributes as `<table>` rows to a dot
10 /// file.
11 pub trait DotAttributes {
12     /// Write this thing's attributes to the given output. Each attribute must
13     /// be its own `<tr>...</tr>`.
dot_attributes<W>( &self, ctx: &BindgenContext, out: &mut W, ) -> io::Result<()> where W: io::Write14     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.
write_dot_file<P>(ctx: &BindgenContext, path: P) -> io::Result<()> where P: AsRef<Path>,24 pub fn write_dot_file<P>(ctx: &BindgenContext, path: P) -> io::Result<()>
25 where
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