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