Skip to main content

zoi_cli/cmd/
why.rs

1//! Logic for the `why` command.
2
3use anyhow::{Result, anyhow};
4use colored::Colorize;
5
6use crate::pkg::{local, resolve, types};
7
8/// Run the why command.
9///
10/// # Errors
11///
12/// Returns an error if the package is not installed or if there is an error
13/// reading the package metadata or dependents.
14pub fn run(package_name: &str) -> Result<()> {
15    let request = resolve::parse_source_string(package_name)?;
16    let mut candidates = Vec::new();
17    for scope in [
18        types::Scope::User,
19        types::Scope::System,
20        types::Scope::Project
21    ] {
22        candidates
23            .extend(local::find_installed_manifests_matching(&request, scope)?);
24    }
25    if candidates.is_empty() {
26        return Err(anyhow!("Package '{package_name}' is not installed."));
27    }
28    let manifest = crate::cmd::installed_select::choose_installed_manifest(
29        package_name,
30        &candidates,
31        false
32    )?;
33
34    let pkg_dir = local::get_package_dir(
35        manifest.scope,
36        &manifest.registry_handle,
37        &manifest.repo,
38        &manifest.name
39    )?;
40    let mut reasons = Vec::new();
41
42    if manifest.reason == types::InstallReason::Direct {
43        reasons.push("it was installed directly by the user".to_string());
44    }
45
46    let mut dependents = local::get_dependents(&pkg_dir)?;
47
48    if !dependents.is_empty() {
49        dependents.sort();
50        reasons.push(format!(
51            "it is a dependency for: {}",
52            dependents.join(", ").cyan()
53        ));
54    }
55
56    if reasons.is_empty() {
57        if matches!(manifest.reason, types::InstallReason::Dependency { .. }) {
58            println!(
59                "Package '{}' is installed as a dependency, but no packages \
60                 list it as a requirement. It may be an orphan.",
61                local::installed_manifest_source(&manifest).bold()
62            );
63        } else {
64            println!(
65                "Package '{}' is installed, but its installation reason is \
66                 unclear.",
67                local::installed_manifest_source(&manifest).bold()
68            );
69        }
70    } else {
71        println!(
72            "Package '{}' is installed because {}.",
73            local::installed_manifest_source(&manifest).bold(),
74            reasons.join(" and ")
75        );
76    }
77
78    Ok(())
79}