1use crate::cli::{SearchCommands, SearchProviderCommands};
4use crate::search::{SearchConfig, SearchEngine, SearchProviderType};
5use anyhow::Result;
6use colored::*;
7
8pub async fn handle(command: SearchCommands) -> Result<()> {
10 match command {
11 SearchCommands::Provider { command } => handle_provider(command).await,
12 SearchCommands::Query {
13 provider,
14 query,
15 format,
16 count,
17 } => handle_query(provider, query, format, count).await,
18 }
19}
20
21async fn handle_provider(command: SearchProviderCommands) -> Result<()> {
22 let mut config = SearchConfig::load()?;
23
24 match command {
25 SearchProviderCommands::Add { name, url } => {
26 println!(
27 "{} Adding search provider '{}' with URL: {}",
28 "🔍".blue(),
29 name.bold(),
30 url.dimmed()
31 );
32
33 match SearchProviderType::detect_from_url(&url) {
35 Ok(provider_type) => {
36 println!(
37 " {} Auto-detected provider type: {}",
38 "✓".green(),
39 format!("{:?}", provider_type).cyan()
40 );
41
42 config.add_provider(name.clone(), url, provider_type.clone())?;
43 config.save()?;
44
45 println!(
46 "{} Search provider '{}' added successfully",
47 "✓".green(),
48 name.bold()
49 );
50
51 show_provider_setup_help(&name, provider_type);
53 }
54 Err(e) => {
55 eprintln!(
56 "{} Failed to detect provider type from URL: {}",
57 "✗".red(),
58 e
59 );
60 eprintln!(
61 " {} Make sure the URL matches one of the supported patterns:",
62 "ℹ".blue()
63 );
64 eprintln!(" • Brave: api.search.brave.com");
65 eprintln!(" • Exa: api.exa.ai");
66 eprintln!(" • Serper: google.serper.dev");
67 eprintln!(" • SerpApi: serpapi.com");
68 eprintln!(" • DuckDuckGo: api.duckduckgo.com");
69 eprintln!(" • Jina: s.jina.ai");
70 eprintln!(" • Tavily: api.tavily.com");
71 }
72 }
73 }
74 SearchProviderCommands::List => {
75 let providers = config.list_providers();
76
77 if providers.is_empty() {
78 println!("{} No search providers configured", "📋".blue());
79 println!(
80 "\n{}",
81 "Add one with: lc search provider add <name> <url>".italic().dimmed()
82 );
83 } else {
84 println!("{} Configured search providers:", "📋".blue());
85 println!();
86
87 for (name, provider) in providers {
88 let is_default = config.get_default_provider() == Some(name);
89 let default_marker = if is_default { " (default)" } else { "" };
90
91 println!(
92 " {} {}{}",
93 "•".cyan(),
94 name.bold(),
95 default_marker.green()
96 );
97 println!(" Type: {:?}", provider.provider_type);
98 println!(" URL: {}", provider.url.dimmed());
99
100 if !provider.headers.is_empty() {
101 println!(" Headers:");
102 for (key, _) in &provider.headers {
103 println!(" - {}: ***", key.yellow());
104 }
105 } else {
106 println!(" Headers: {}", "None configured".dimmed());
107 }
108 println!();
109 }
110 }
111 }
112 SearchProviderCommands::Delete { name } => {
113 println!(
114 "{} Removing search provider '{}'...",
115 "🗑".red(),
116 name.bold()
117 );
118
119 config.delete_provider(&name)?;
120 config.save()?;
121
122 println!(
123 "{} Search provider '{}' removed successfully",
124 "✓".green(),
125 name
126 );
127 }
128 SearchProviderCommands::Set {
129 provider,
130 header_name,
131 header_value,
132 } => {
133 println!(
134 "{} Setting header '{}' for provider '{}'",
135 "⚙".blue(),
136 header_name.yellow(),
137 provider.bold()
138 );
139
140 config.set_header(&provider, header_name.clone(), header_value)?;
141 config.save()?;
142
143 println!(
144 "{} Header '{}' set successfully for '{}'",
145 "✓".green(),
146 header_name.yellow(),
147 provider.bold()
148 );
149 }
150 }
151
152 Ok(())
153}
154
155async fn handle_query(provider: String, query: String, format: String, count: usize) -> Result<()> {
156 println!(
157 "{} Searching with '{}' for: {}",
158 "🔍".blue(),
159 provider.bold(),
160 query.cyan()
161 );
162
163 let engine = SearchEngine::new()?;
164 let results = engine.search(&provider, &query, Some(count)).await?;
165
166 match format.as_str() {
167 "json" => {
168 println!("{}", engine.format_results_json(&results)?);
169 }
170 "md" | "markdown" => {
171 println!("{}", engine.format_results_markdown(&results));
172 }
173 _ => {
174 eprintln!(
175 "{} Invalid format '{}'. Use 'json' or 'md'/'markdown'",
176 "✗".red(),
177 format
178 );
179 }
180 }
181
182 Ok(())
183}
184
185fn show_provider_setup_help(name: &str, provider_type: SearchProviderType) {
186 println!();
187 println!("{} Next steps:", "ℹ".blue());
188
189 match provider_type {
190 SearchProviderType::Brave => {
191 println!(" 1. Get your API key from: https://brave.com/search/api/");
192 println!(" 2. Set the API key:");
193 println!(
194 " {}",
195 format!("lc search provider set {} X-Subscription-Token YOUR_API_KEY", name).bold()
196 );
197 }
198 SearchProviderType::Exa => {
199 println!(" 1. Get your API key from: https://exa.ai/");
200 println!(" 2. Set the API key:");
201 println!(
202 " {}",
203 format!("lc search provider set {} x-api-key YOUR_API_KEY", name).bold()
204 );
205 }
206 SearchProviderType::Serper => {
207 println!(" 1. Get your API key from: https://serper.dev/");
208 println!(" 2. Set the API key:");
209 println!(
210 " {}",
211 format!("lc search provider set {} X-API-KEY YOUR_API_KEY", name).bold()
212 );
213 }
214 SearchProviderType::SerpApi => {
215 println!(" 1. Get your API key from: https://serpapi.com/");
216 println!(" 2. Set the API key:");
217 println!(
218 " {}",
219 format!("lc search provider set {} api_key YOUR_API_KEY", name).bold()
220 );
221 }
222 SearchProviderType::DuckDuckGo => {
223 println!(" {} No API key required! You can start searching immediately:", "✓".green());
224 println!(
225 " {}",
226 format!("lc search query {} \"your query\"", name).bold()
227 );
228 }
229 SearchProviderType::Jina => {
230 println!(" 1. Get your API key from: https://jina.ai/");
231 println!(" 2. Set the API key:");
232 println!(
233 " {}",
234 format!("lc search provider set {} Authorization \"Bearer YOUR_API_KEY\"", name).bold()
235 );
236 println!(" 3. (Optional) Enable full content reading:");
237 println!(
238 " {}",
239 format!("lc search provider set {} X-Engine direct", name).bold()
240 );
241 println!(" 4. (Optional) Enable JSON response:");
242 println!(
243 " {}",
244 format!("lc search provider set {} Accept application/json", name).bold()
245 );
246 }
247 SearchProviderType::Tavily => {
248 println!(" 1. Get your API key from: https://tavily.com/");
249 println!(" 2. Set the API key:");
250 println!(
251 " {}",
252 format!("lc search provider set {} api-key YOUR_API_KEY", name).bold()
253 );
254 }
255 }
256
257 println!();
258}