chaud_hot/workspace/graph/
def.rs1use super::{BuildEnv, Krate, KrateIdx, KrateIndex};
4use crate::cargo::Cargo;
5use crate::cargo::metadata::{ManifestPath, Metadata};
6use crate::util::assert::err_assert;
7use anyhow::{Context as _, Result};
8use core::ops;
9use hashbrown::HashSet;
10use std::collections::VecDeque;
11
12pub struct Graph {
13 env: BuildEnv,
14 krates: Box<[Krate]>,
15}
16
17impl ops::Index<KrateIdx> for Graph {
18 type Output = Krate;
19
20 #[expect(
21 clippy::indexing_slicing,
22 reason = "crate indices are assumed to be valid"
23 )]
24 fn index(&self, idx: KrateIdx) -> &Self::Output {
25 &self.krates[idx.usize()]
26 }
27}
28
29impl Graph {
30 pub fn new(
31 root_mani: ManifestPath,
32 feature_flags: Option<&'static str>,
33 ) -> Result<&'static Self> {
34 new_inner(root_mani, feature_flags).context("Failed to load crate graph")
35 }
36
37 pub fn env(&self) -> &BuildEnv {
38 &self.env
39 }
40
41 pub fn collect_krates_to_watch(&self) -> impl Iterator<Item = &Krate> {
42 collect_inner(self).into_iter().map(|k| &self[k])
43 }
44}
45
46fn new_inner(
47 root_mani: ManifestPath,
48 feature_flags: Option<&'static str>,
49) -> Result<&'static Graph> {
50 let cargo = Cargo::new(root_mani);
51 let meta = cargo.load_metadata()?;
52
53 let index = KrateIndex::new(meta.packages())?;
54 let env = BuildEnv::new(cargo, feature_flags, &meta, &index)?;
55 let krates = load_krates(&meta, &env, &index)?;
56
57 Ok(Box::leak(Box::new(Graph { env, krates })))
58}
59
60fn load_krates(meta: &Metadata, env: &BuildEnv, index: &KrateIndex) -> Result<Box<[Krate]>> {
61 let mut krates = vec![];
62
63 for pkg in meta.packages() {
64 krates.push(Krate::new(env, index, pkg)?);
65 }
66
67 krates.sort_unstable_by_key(|k| k.idx());
68 for (pos, krate) in krates.iter().enumerate() {
69 err_assert!(pos == krate.idx().usize());
70 }
71
72 Ok(krates.into_boxed_slice())
73}
74
75fn collect_inner(graph: &Graph) -> Box<[KrateIdx]> {
76 let mut found = vec![];
77 let mut seen = HashSet::new();
78 let mut queue = VecDeque::new();
79 seen.insert(graph.env.root());
80 queue.push_back(graph.env.root());
81
82 while let Some(idx) = queue.pop_front() {
83 found.push(idx);
84 for &dep in graph[idx].deps() {
85 if seen.insert(dep) {
86 queue.push_back(dep);
87 }
88 }
89 }
90
91 found.into_boxed_slice()
92}