vx_cli/commands/
search.rs1use crate::cli::OutputFormat;
4use crate::ui::UI;
5use std::collections::HashMap;
6use vx_core::PluginRegistry;
7use vx_core::Result;
8
9pub async fn handle(
10 registry: &PluginRegistry,
11 query: Option<String>,
12 category: Option<String>,
13 _installed_only: bool,
14 _available_only: bool,
15 format: OutputFormat,
16 verbose: bool,
17) -> Result<()> {
18 let spinner = UI::new_spinner("Searching tools...");
19
20 let mut all_tools = Vec::new();
22 for plugin in registry.get_plugins() {
23 for tool in plugin.tools() {
24 all_tools.push((
25 tool.name().to_string(),
26 tool.description().to_string(),
27 plugin.name().to_string(),
28 ));
29 }
30 }
31
32 spinner.finish_and_clear();
33
34 let filtered_tools: Vec<_> = all_tools
36 .into_iter()
37 .filter(|(name, description, _plugin)| {
38 if let Some(ref q) = query {
40 let q_lower = q.to_lowercase();
41 if !name.to_lowercase().contains(&q_lower)
42 && !description.to_lowercase().contains(&q_lower)
43 {
44 return false;
45 }
46 }
47
48 if let Some(ref cat) = category {
50 let cat_lower = cat.to_lowercase();
51 let tool_category = get_tool_category(name);
53 if !tool_category.to_lowercase().contains(&cat_lower) {
54 return false;
55 }
56 }
57
58 true
59 })
60 .collect();
61
62 match format {
64 OutputFormat::Table => display_table(&filtered_tools, verbose, &query),
65 OutputFormat::Json => display_json(&filtered_tools),
66 OutputFormat::Yaml => display_yaml(&filtered_tools),
67 }
68
69 Ok(())
70}
71
72fn get_tool_category(tool_name: &str) -> &'static str {
73 match tool_name {
74 "node" | "npm" | "npx" => "javascript",
75 "go" => "go",
76 "cargo" => "rust",
77 "uv" | "uvx" => "python",
78 _ => "utility",
79 }
80}
81
82fn display_table(tools: &[(String, String, String)], verbose: bool, query: &Option<String>) {
83 if let Some(q) = query {
84 UI::info(&format!("Search results for: {}", q));
85 } else {
86 UI::info("Available tools:");
87 }
88
89 if tools.is_empty() {
90 UI::warn("No tools found matching the criteria");
91 return;
92 }
93
94 println!();
95 if verbose {
96 for (name, description, plugin) in tools {
97 let category = get_tool_category(name);
98 println!("{} ({})", name, category);
99 println!(" Plugin: {}", plugin);
100 println!(" Description: {}", description);
101 println!(" Status: Available"); println!();
103 }
104 } else {
105 println!("┌─────────────┬─────────────┬─────────────────────────────────────┐");
107 println!("│ Tool │ Category │ Description │");
108 println!("├─────────────┼─────────────┼─────────────────────────────────────┤");
109
110 for (name, description, _plugin) in tools {
111 let category = get_tool_category(name);
112 let truncated_desc = if description.len() > 35 {
113 format!("{}...", &description[..32])
114 } else {
115 description.clone()
116 };
117
118 println!(
119 "│ {:<11} │ {:<11} │ {:<35} │",
120 name, category, truncated_desc
121 );
122 }
123
124 println!("└─────────────┴─────────────┴─────────────────────────────────────┘");
125 }
126
127 UI::info(&format!("Found {} tools", tools.len()));
128}
129
130fn display_json(tools: &[(String, String, String)]) {
131 let mut json_tools = Vec::new();
132
133 for (name, description, plugin) in tools {
134 let mut tool_info = HashMap::new();
135 tool_info.insert("name", name.clone());
136 tool_info.insert("description", description.clone());
137 tool_info.insert("plugin", plugin.clone());
138 tool_info.insert("category", get_tool_category(name).to_string());
139 tool_info.insert("status", "available".to_string()); json_tools.push(tool_info);
141 }
142
143 match serde_json::to_string_pretty(&json_tools) {
144 Ok(json) => println!("{}", json),
145 Err(e) => UI::error(&format!("Failed to serialize to JSON: {}", e)),
146 }
147}
148
149fn display_yaml(tools: &[(String, String, String)]) {
150 let mut yaml_tools = Vec::new();
151
152 for (name, description, plugin) in tools {
153 let mut tool_info = HashMap::new();
154 tool_info.insert("name", name.clone());
155 tool_info.insert("description", description.clone());
156 tool_info.insert("plugin", plugin.clone());
157 tool_info.insert("category", get_tool_category(name).to_string());
158 tool_info.insert("status", "available".to_string()); yaml_tools.push(tool_info);
160 }
161
162 match serde_yaml::to_string(&yaml_tools) {
163 Ok(yaml) => println!("{}", yaml),
164 Err(e) => UI::error(&format!("Failed to serialize to YAML: {}", e)),
165 }
166}