swink_agent_plugin_web/search/
mod.rs1use std::pin::Pin;
2
3use serde::{Deserialize, Serialize};
4use thiserror::Error;
5
6#[cfg(feature = "brave")]
7mod brave;
8#[cfg(feature = "duckduckgo")]
9mod duckduckgo;
10#[cfg(feature = "tavily")]
11mod tavily;
12
13#[cfg(feature = "brave")]
14pub use brave::BraveProvider;
15#[cfg(feature = "duckduckgo")]
16pub use duckduckgo::DuckDuckGoProvider;
17#[cfg(feature = "tavily")]
18pub use tavily::TavilyProvider;
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct SearchResult {
23 pub title: String,
24 pub url: String,
25 pub snippet: String,
26}
27
28#[derive(Debug, Error)]
30pub enum SearchError {
31 #[error("Network error: {0}")]
32 NetworkError(String),
33 #[error("Failed to parse response: {0}")]
34 ParseError(String),
35 #[error("Rate limited by search provider")]
36 RateLimited,
37 #[error("API key is required but not configured")]
38 ApiKeyMissing,
39 #[error("Search provider unavailable: {0}")]
40 ProviderUnavailable(String),
41}
42
43pub trait SearchProvider: Send + Sync {
45 fn name(&self) -> &str;
46 fn search(
47 &self,
48 query: &str,
49 max_results: usize,
50 ) -> Pin<
51 Box<dyn std::future::Future<Output = Result<Vec<SearchResult>, SearchError>> + Send + '_>,
52 >;
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn search_result_is_publicly_constructible() {
61 let result = SearchResult {
62 title: "Rust".to_string(),
63 url: "https://www.rust-lang.org".to_string(),
64 snippet: "Systems programming language".to_string(),
65 };
66 assert_eq!(result.title, "Rust");
67 }
68}