Skip to main content

zoi_cli/cmd/
provides.rs

1//! Searching for packages that provide a specific file or command.
2
3use anyhow::Result;
4use colored::Colorize;
5use comfy_table::presets::UTF8_FULL;
6use comfy_table::{Attribute, Cell, ContentArrangement, Table};
7use rayon::prelude::*;
8
9use crate::pkg::{config, db};
10
11/// Searches for packages that provide a specific file or command.
12///
13/// This queries the configured registries to find which packages contain the
14/// given term in their file lists.
15///
16/// # Errors
17///
18/// This function will return an error if:
19/// - It fails to read the Zoi configuration.
20/// - It fails to connect to the package database or execute the query.
21/// # Errors
22///
23/// Returns an error if the provides search fails.
24pub fn run(term: &str) -> Result<()> {
25    println!(
26        "{} Searching for packages providing '{}'...",
27        "::".bold().blue(),
28        term.cyan().bold()
29    );
30
31    let config = config::read_config()?;
32    let mut registries = Vec::new();
33    if let Some(default) = &config.default_registry {
34        registries.push(default.handle.clone());
35    }
36    for reg in &config.added_registries {
37        registries.push(reg.handle.clone());
38    }
39
40    let all_results: Vec<(crate::pkg::types::Package, String)> = registries
41        .into_par_iter()
42        .filter_map(|handle| db::find_provides(&handle, term).ok())
43        .flatten()
44        .collect();
45
46    if all_results.is_empty() {
47        println!(
48            "\n{} No packages found providing this item.",
49            "::".bold().yellow()
50        );
51        println!(
52            "   {} Ensure you have run 'zoi sync --files' to index remote \
53             file lists.",
54            "Hint:".cyan()
55        );
56        return Ok(());
57    }
58
59    let mut table = Table::new();
60    table
61        .load_style(UTF8_FULL)
62        .set_content_arrangement(ContentArrangement::Dynamic)
63        .set_header(vec![
64            Cell::new("Package").add_attribute(Attribute::Bold),
65            Cell::new("Version").add_attribute(Attribute::Bold),
66            Cell::new("Matches").add_attribute(Attribute::Bold),
67            Cell::new("Repo").add_attribute(Attribute::Bold),
68        ]);
69
70    for (pkg, matched_path) in all_results {
71        let repo_display = &pkg.repo;
72        table.add_row(vec![
73            Cell::new(pkg.name).fg(comfy_table::Color::Cyan),
74            Cell::new(pkg.version.unwrap_or_else(|| "N/A".to_string()))
75                .fg(comfy_table::Color::Yellow),
76            Cell::new(matched_path).fg(comfy_table::Color::Green),
77            Cell::new(repo_display.clone()).fg(comfy_table::Color::DarkGrey),
78        ]);
79    }
80
81    println!("{table}");
82
83    Ok(())
84}