1use crate::pkg::install::resolver;
2use anyhow::{Result, anyhow};
3use colored::*;
4use std::collections::HashSet;
5
6pub fn run(package_names: &[String]) -> Result<()> {
7 if package_names.is_empty() {
8 println!("{}", "Please specify at least one package name.".yellow());
9 return Ok(());
10 }
11
12 println!("{} Resolving dependency tree...", "::".bold().blue());
13
14 let (graph, non_zoi_deps) = resolver::resolve_dependency_graph(
15 package_names,
16 None,
17 false,
18 true,
19 true,
20 None,
21 true,
22 None,
23 )?;
24
25 if !non_zoi_deps.is_empty() {
26 println!(
27 "\n{} External dependencies (non-Zoi):",
28 "::".bold().yellow()
29 );
30 for dep in non_zoi_deps {
31 println!(" - {}", dep.dimmed());
32 }
33 }
34
35 println!("\n{} Dependency tree:", "::".bold().blue());
36
37 let mut visited = HashSet::new();
38 for source in package_names {
39 if let Some(children) = graph.adj.get("$root") {
40 for pkg_id in children {
41 let Some(node) = graph.nodes.get(pkg_id) else {
42 continue;
43 };
44 if source.contains(&node.pkg.name) {
45 print_node(&graph, pkg_id, "", true, &mut visited)?;
46 }
47 }
48 }
49 }
50
51 Ok(())
52}
53
54fn print_node(
55 graph: &resolver::DependencyGraph,
56 pkg_id: &str,
57 prefix: &str,
58 is_last: bool,
59 visited: &mut HashSet<String>,
60) -> Result<()> {
61 let node = graph
62 .nodes
63 .get(pkg_id)
64 .ok_or_else(|| anyhow!("Package not found in graph: {}", pkg_id))?;
65 let is_repeated = visited.contains(pkg_id);
66
67 let connector = if is_last { "└── " } else { "├── " };
68
69 let pkg_display = if let Some(sub) = &node.sub_package {
70 format!("{}:{}", node.pkg.name.cyan().bold(), sub.yellow())
71 } else {
72 node.pkg.name.cyan().bold().to_string()
73 };
74
75 let version_display = format!("v{}", node.version);
76 let repeated_mark = if is_repeated {
77 " (*)".dimmed()
78 } else {
79 "".normal()
80 };
81
82 println!(
83 "{}{}{}{} {}",
84 prefix, connector, pkg_display, repeated_mark, version_display
85 );
86
87 if is_repeated {
88 return Ok(());
89 }
90 visited.insert(pkg_id.to_string());
91
92 if let Some(children) = graph.adj.get(pkg_id) {
93 let child_count = children.len();
94 let mut sorted_children: Vec<_> = children.iter().collect();
95 sorted_children.sort();
96
97 for (i, child_id) in sorted_children.iter().enumerate() {
98 let new_prefix = format!("{}{}", prefix, if is_last { " " } else { "│ " });
99 print_node(graph, child_id, &new_prefix, i == child_count - 1, visited)?;
100 }
101 }
102
103 Ok(())
104}