1use crate::config::RootConfig;
2use anyhow::{Result, bail};
3use std::collections::HashSet;
4
5pub fn resolve_packages(root: &RootConfig, requested: &[&str]) -> Result<Vec<String>> {
9 let mut resolved: Vec<String> = Vec::new();
10 let mut seen: HashSet<String> = HashSet::new();
11
12 for pkg in requested {
13 resolve_one(root, pkg, &mut resolved, &mut seen, &mut Vec::new())?;
14 }
15
16 Ok(resolved)
17}
18
19fn resolve_one(
20 root: &RootConfig,
21 pkg: &str,
22 resolved: &mut Vec<String>,
23 seen: &mut HashSet<String>,
24 stack: &mut Vec<String>,
25) -> Result<()> {
26 if seen.contains(pkg) {
27 return Ok(());
28 }
29
30 if stack.contains(&pkg.to_string()) {
31 stack.push(pkg.to_string());
32 bail!("circular dependency detected: {}", stack.join(" -> "));
33 }
34
35 let pkg_config = root.packages.get(pkg);
36 if let Some(config) = pkg_config {
37 stack.push(pkg.to_string());
38 for dep in &config.depends {
39 if !root.packages.contains_key(dep.as_str()) {
40 bail!("package '{pkg}' depends on unknown package '{dep}'");
41 }
42 resolve_one(root, dep, resolved, seen, stack)?;
43 }
44 stack.pop();
45 } else {
46 bail!("unknown package: '{pkg}'");
47 }
48
49 seen.insert(pkg.to_string());
50 resolved.push(pkg.to_string());
51 Ok(())
52}