Skip to main content

swink_agent_plugin_web/search/
mod.rs

1use 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/// A single search result.
21#[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    /// Construct a new `SearchResult`.
31    #[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/// Errors from search providers.
46#[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
61/// Abstraction over different search backends.
62pub 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}