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#[non_exhaustive]
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct SearchResult {
24 pub title: String,
25 pub url: String,
26 pub snippet: String,
27}
28
29impl SearchResult {
30 #[must_use]
32 pub fn new(
33 title: impl Into<String>,
34 url: impl Into<String>,
35 snippet: impl Into<String>,
36 ) -> Self {
37 Self {
38 title: title.into(),
39 url: url.into(),
40 snippet: snippet.into(),
41 }
42 }
43}
44
45#[non_exhaustive]
47#[derive(Debug, Error)]
48pub enum SearchError {
49 #[error("Network error: {0}")]
50 NetworkError(String),
51 #[error("Failed to parse response: {0}")]
52 ParseError(String),
53 #[error("Rate limited by search provider")]
54 RateLimited,
55 #[error("API key is required but not configured")]
56 ApiKeyMissing,
57 #[error("Search provider unavailable: {0}")]
58 ProviderUnavailable(String),
59}
60
61pub trait SearchProvider: Send + Sync {
63 fn name(&self) -> &str;
64 fn search(
65 &self,
66 query: &str,
67 max_results: usize,
68 ) -> Pin<
69 Box<dyn std::future::Future<Output = Result<Vec<SearchResult>, SearchError>> + Send + '_>,
70 >;
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn search_result_is_publicly_constructible() {
79 let result = SearchResult {
80 title: "Rust".to_string(),
81 url: "https://www.rust-lang.org".to_string(),
82 snippet: "Systems programming language".to_string(),
83 };
84 assert_eq!(result.title, "Rust");
85 }
86}