rgen_cli_lib/cmds/
search.rs

1use anyhow::Result;
2use clap::Args;
3use colored::*;
4use rgen_core::RegistryClient;
5use serde_json;
6
7#[derive(Args, Debug)]
8pub struct SearchArgs {
9    /// Search query (searches in name, description, keywords, and tags)
10    pub query: String,
11
12    /// Filter by category (e.g., "rust", "python", "web", "cli")
13    #[arg(short, long)]
14    pub category: Option<String>,
15
16    /// Filter by keyword (e.g., "api", "database", "auth")
17    #[arg(short, long)]
18    pub keyword: Option<String>,
19
20    /// Filter by author/owner
21    #[arg(short, long)]
22    pub author: Option<String>,
23
24    /// Show only stable versions
25    #[arg(long)]
26    pub stable: bool,
27
28    /// Limit number of results
29    #[arg(short, long, default_value = "20")]
30    pub limit: usize,
31
32    /// Output results as JSON
33    #[arg(long)]
34    pub json: bool,
35
36    /// Show detailed information for each result
37    #[arg(short, long)]
38    pub detailed: bool,
39}
40
41pub async fn run(args: &SearchArgs) -> Result<()> {
42    let registry_client = RegistryClient::new()?;
43
44    // Build search parameters
45    let search_params = rgen_core::registry::SearchParams {
46        query: &args.query,
47        category: args.category.as_deref(),
48        keyword: args.keyword.as_deref(),
49        author: args.author.as_deref(),
50        stable_only: args.stable,
51        limit: args.limit,
52    };
53
54    let results = registry_client.advanced_search(&search_params).await?;
55
56    if args.json {
57        // Output JSON format
58        let json_output = serde_json::to_string_pretty(&results)?;
59        println!("{}", json_output);
60    } else {
61        // Output human-readable format
62        if results.is_empty() {
63            println!("No rpacks found matching your criteria");
64            return Ok(());
65        }
66
67        println!("Found {} rpack(s):", results.len());
68        println!();
69
70        if args.detailed {
71            print_detailed_results(&results);
72        } else {
73            print_summary_results(&results);
74        }
75    }
76
77    Ok(())
78}
79
80fn print_summary_results(results: &[rgen_core::SearchResult]) {
81    // Header with colors
82    println!(
83        "{:<40} {:<12} {:<20} {}",
84        "ID".bold(),
85        "LATEST".bold(),
86        "TAGS".bold(),
87        "DESCRIPTION".bold()
88    );
89    println!("{}", "-".repeat(80).dimmed());
90
91    // Results
92    for result in results {
93        let tags = result
94            .tags
95            .iter()
96            .map(|tag| format!("{}", tag.cyan()))
97            .collect::<Vec<_>>()
98            .join(", ");
99
100        let description = if result.description.len() > 30 {
101            format!("{}...", &result.description[..27])
102        } else {
103            result.description.clone()
104        };
105
106        println!(
107            "{:<40} {:<12} {:<20} {}",
108            result.id.green(),
109            result.latest_version.yellow(),
110            tags,
111            description
112        );
113    }
114}
115
116fn print_detailed_results(results: &[rgen_core::SearchResult]) {
117    for (i, result) in results.iter().enumerate() {
118        if i > 0 {
119            println!();
120        }
121
122        println!("{}", format!("{}. {}", i + 1, result.name).bold().green());
123        println!("  ID: {}", result.id.cyan());
124        println!("  Version: {}", result.latest_version.yellow());
125        println!("  Description: {}", result.description);
126
127        if !result.tags.is_empty() {
128            let tags = result
129                .tags
130                .iter()
131                .map(|tag| format!("{}", tag.cyan()))
132                .collect::<Vec<_>>()
133                .join(", ");
134            println!("  Tags: {}", tags);
135        }
136
137        if let Some(category) = &result.category {
138            println!("  Category: {}", category.magenta());
139        }
140
141        if let Some(author) = &result.author {
142            println!("  Author: {}", author.blue());
143        }
144
145        if let Some(downloads) = result.downloads {
146            println!("  Downloads: {}", format!("{}", downloads).dimmed());
147        }
148
149        if let Some(updated) = &result.updated {
150            println!("  Updated: {}", updated.format("%Y-%m-%d %H:%M:%S UTC"));
151        }
152    }
153}