1use dot::{Id, LabelText};
2use ide_db::base_db::all_crates;
3use ide_db::base_db::salsa::plumbing::AsId;
4use ide_db::{
5 FxHashMap, RootDatabase,
6 base_db::{BuiltCrateData, BuiltDependency, Crate, ExtraCrateData, SourceDatabase},
7};
8
9pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> String {
20 let all_crates = all_crates(db);
21 let crates_to_render = all_crates
22 .iter()
23 .copied()
24 .map(|krate| (krate, (krate.data(db), krate.extra_data(db))))
25 .filter(|(_, (crate_data, _))| {
26 if full {
27 true
28 } else {
29 let root_id = db.file_source_root(crate_data.root_file_id).source_root_id(db);
31 !db.source_root(root_id).source_root(db).is_library
32 }
33 })
34 .collect();
35 let graph = DotCrateGraph { crates_to_render };
36
37 let mut dot = Vec::new();
38 dot::render(&graph, &mut dot).unwrap();
39 String::from_utf8(dot).unwrap()
40}
41
42struct DotCrateGraph<'db> {
43 crates_to_render: FxHashMap<Crate, (&'db BuiltCrateData, &'db ExtraCrateData)>,
44}
45
46type Edge<'a> = (Crate, &'a BuiltDependency);
47
48impl<'a> dot::GraphWalk<'a, Crate, Edge<'a>> for DotCrateGraph<'_> {
49 fn nodes(&'a self) -> dot::Nodes<'a, Crate> {
50 self.crates_to_render.keys().copied().collect()
51 }
52
53 fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> {
54 self.crates_to_render
55 .iter()
56 .flat_map(|(krate, (crate_data, _))| {
57 crate_data
58 .dependencies
59 .iter()
60 .filter(|dep| self.crates_to_render.contains_key(&dep.crate_id))
61 .map(move |dep| (*krate, dep))
62 })
63 .collect()
64 }
65
66 fn source(&'a self, edge: &Edge<'a>) -> Crate {
67 edge.0
68 }
69
70 fn target(&'a self, edge: &Edge<'a>) -> Crate {
71 edge.1.crate_id
72 }
73}
74
75impl<'a> dot::Labeller<'a, Crate, Edge<'a>> for DotCrateGraph<'_> {
76 fn graph_id(&'a self) -> Id<'a> {
77 Id::new("rust_analyzer_crate_graph").unwrap()
78 }
79
80 fn node_id(&'a self, n: &Crate) -> Id<'a> {
81 let id = n.as_id().index();
82 Id::new(format!("_{id:?}")).unwrap()
83 }
84
85 fn node_shape(&'a self, _node: &Crate) -> Option<LabelText<'a>> {
86 Some(LabelText::LabelStr("box".into()))
87 }
88
89 fn node_label(&'a self, n: &Crate) -> LabelText<'a> {
90 let name = self.crates_to_render[n]
91 .1
92 .display_name
93 .as_ref()
94 .map_or("(unnamed crate)", |name| name.as_str());
95 LabelText::LabelStr(name.into())
96 }
97}