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(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. |
24 | pub(crate) 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 | |