1use crate::pkg::{local, resolve, types};
2use crate::utils;
3use anyhow::Result;
4use colored::*;
5use std::fs;
6
7fn print_dependency_group(group: &types::DependencyGroup, indent: usize) {
8 let prefix = " ".repeat(indent * 2);
9 let mut count = 0;
10
11 let required = group.get_required_simple();
12 if !required.is_empty() {
13 for dep in required {
14 println!("{}- {} (required)", prefix, dep);
15 count += 1;
16 }
17 }
18
19 let options = group.get_required_options();
20 if !options.is_empty() {
21 for opt_group in options {
22 println!(
23 "{}{}: {} (choose {})",
24 prefix,
25 opt_group.name.bold(),
26 opt_group.desc,
27 if opt_group.all { "any" } else { "one" }
28 );
29 for dep in &opt_group.depends {
30 println!("{} - {}", prefix, dep);
31 count += 1;
32 }
33 }
34 }
35
36 let optional = group.get_optional();
37 if !optional.is_empty() {
38 for dep in optional {
39 println!("{}- {} (optional)", prefix, dep);
40 count += 1;
41 }
42 }
43
44 if count == 0 {
45 println!("{}- {}", prefix, "None".italic());
46 }
47}
48
49pub fn run(source: &str, raw: bool) -> Result<()> {
50 let source = source.trim();
51 let resolved_source = resolve::resolve_source(source, false)?;
52
53 if raw {
54 let content = fs::read_to_string(&resolved_source.path)?;
55 println!("{content}");
56 return Ok(());
57 }
58 let mut pkg: types::Package = crate::pkg::lua::parser::parse_lua_package(
59 resolved_source.path.to_str().unwrap(),
60 None,
61 false,
62 )?;
63 if let Some(repo_name) = resolved_source.repo_name {
64 pkg.repo = repo_name;
65 }
66 pkg.version = Some(
67 resolve::get_default_version(&pkg, resolved_source.registry_handle.as_deref())
68 .unwrap_or_else(|_| "N/A".to_string()),
69 );
70
71 let request = resolve::parse_source_string(source)?;
72
73 let installed_manifest =
74 match local::is_package_installed(&pkg.name, request.sub_package.as_deref(), pkg.scope) {
75 Ok(manifest) => manifest,
76 Err(e) => {
77 eprintln!("Warning: could not check installation status: {}", e);
78 None
79 }
80 };
81
82 print_beautiful(&pkg, installed_manifest.as_ref());
83 Ok(())
84}
85
86fn print_beautiful(
87 pkg: &crate::pkg::types::Package,
88 installed_manifest: Option<&types::InstallManifest>,
89) {
90 println!(
91 "{} {} - {}",
92 pkg.name.bold().green(),
93 pkg.version.as_deref().unwrap_or_default().dimmed(),
94 pkg.repo
95 );
96 if let Some(website) = &pkg.website {
97 println!("Website: {}", website.cyan().underline());
98 }
99 if !pkg.git.is_empty() {
100 println!("Git Repo: {}", pkg.git.cyan().underline());
101 }
102 println!("{}", pkg.description);
103
104 if let Some(subs) = &pkg.sub_packages {
105 println!("{}: {}", "Sub-packages".bold(), subs.join(", "));
106 if let Some(main_subs) = &pkg.main_subs {
107 println!("{}: {}", "Main sub-packages".bold(), main_subs.join(", "));
108 }
109 }
110
111 if let Some(manifest) = installed_manifest {
112 let status_text = if let Some(sub) = &manifest.sub_package {
113 format!("Installed ({})", sub)
114 } else {
115 "Installed".to_string()
116 };
117 println!(
118 "{}: {} ({})",
119 "Status".bold(),
120 status_text.green(),
121 manifest.version
122 );
123 } else {
124 println!("{}: {}", "Status".bold(), "Not Installed".red());
125 }
126
127 if !pkg.license.is_empty() {
128 println!("{}: {}", "License".bold(), pkg.license);
129 utils::check_license(&pkg.license);
130 }
131
132 let mut maintainer_line = format!(
133 "{}: {} <{}>",
134 "Maintainer".bold(),
135 pkg.maintainer.name,
136 pkg.maintainer.email
137 );
138 if let Some(website) = &pkg.maintainer.website {
139 maintainer_line.push_str(&format!(" - {}", website.cyan().underline()));
140 }
141 println!("{}", maintainer_line);
142
143 if let Some(author) = &pkg.author {
144 let mut author_line = format!("{}: {}", "Author".bold(), author.name);
145 if let Some(email) = &author.email {
146 author_line.push_str(&format!(" <{}>", email));
147 }
148 if let Some(website) = &author.website {
149 author_line.push_str(&format!(" - {}", website.cyan().underline()));
150 }
151 println!("{}", author_line);
152 }
153
154 let type_display = match pkg.package_type {
155 crate::pkg::types::PackageType::Package => "Package",
156 crate::pkg::types::PackageType::Collection => "Collection",
157 crate::pkg::types::PackageType::App => "App",
158 crate::pkg::types::PackageType::Extension => "Extension",
159 };
160 println!("{}: {}", "Type".bold(), type_display);
161
162 let scope_display = match pkg.scope {
163 crate::pkg::types::Scope::User => "User",
164 crate::pkg::types::Scope::System => "System",
165 crate::pkg::types::Scope::Project => "Project",
166 };
167 println!("{}: {}", "Scope".bold(), scope_display);
168
169 if !pkg.tags.is_empty() {
170 println!("{}: {}", "Tags".bold(), pkg.tags.join(", "));
171 }
172
173 if let Some(bins) = &pkg.bins
174 && !bins.is_empty()
175 {
176 println!("{}: {}", "Provides".bold(), bins.join(", ").green());
177 }
178
179 if let Some(conflicts) = &pkg.conflicts
180 && !conflicts.is_empty()
181 {
182 println!("{}: {}", "Conflicts".bold(), conflicts.join(", ").red());
183 }
184
185 if pkg.package_type == crate::pkg::types::PackageType::Package {
186 println!("{}: {}", "Available types".bold(), pkg.types.join(", "));
187 }
188
189 if let Some(deps) = &pkg.dependencies {
190 println!("\n{}:", "Dependencies".bold());
191
192 if let Some(runtime) = &deps.runtime {
193 println!(" Runtime:");
194 print_dependency_group(runtime, 2);
195 }
196
197 if let Some(build_deps) = &deps.build {
198 println!(" Build Dependencies:");
199 match build_deps {
200 types::BuildDependencies::Group(group) => {
201 print_dependency_group(group, 2);
202 }
203 types::BuildDependencies::Typed(typed_build_deps) => {
204 for (name, group) in &typed_build_deps.types {
205 println!(" {}:", name.cyan());
206 print_dependency_group(group, 3);
207 }
208 }
209 }
210 }
211 }
212}