zoi/cmd/
show.rs

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}