web_search/providers/
web_capture.rs1use async_trait::async_trait;
13
14use super::base::{SearchOptions, SearchProvider, SearchResult};
15use crate::error::SearchError;
16
17pub const SUPPORTED_PROVIDERS: [&str; 5] = web_capture::SEARCH_PROVIDERS;
19
20pub struct WebCaptureProvider {
22 name: String,
23 engine: String,
24 enabled: bool,
25 weight: f64,
26}
27
28impl WebCaptureProvider {
29 pub fn new(engine: impl Into<String>) -> Self {
31 let engine = engine.into();
32 Self {
33 name: format!("wc:{engine}"),
34 engine,
35 enabled: true,
36 weight: 1.0,
37 }
38 }
39
40 pub fn engine(&self) -> &str {
42 &self.engine
43 }
44
45 pub fn adapt_items(&self, items: Vec<web_capture::SearchResultItem>) -> Vec<SearchResult> {
47 items
48 .into_iter()
49 .enumerate()
50 .filter_map(|(index, item)| {
51 if item.url.trim().is_empty() {
52 return None;
53 }
54
55 Some(SearchResult {
56 title: if item.title.trim().is_empty() {
57 "Untitled".to_string()
58 } else {
59 item.title
60 },
61 url: item.url,
62 snippet: item.snippet,
63 source: self.name.clone(),
64 rank: if item.rank == 0 { index + 1 } else { item.rank },
65 score: None,
66 sources: None,
67 })
68 })
69 .collect()
70 }
71}
72
73impl Default for WebCaptureProvider {
74 fn default() -> Self {
75 Self::new("wikipedia")
76 }
77}
78
79#[async_trait]
80impl SearchProvider for WebCaptureProvider {
81 fn name(&self) -> &str {
82 &self.name
83 }
84
85 fn is_available(&self) -> bool {
86 self.enabled
87 }
88
89 fn weight(&self) -> f64 {
90 self.weight
91 }
92
93 fn set_weight(&mut self, weight: f64) {
94 self.weight = weight.clamp(0.0, 1.0);
95 }
96
97 fn set_enabled(&mut self, enabled: bool) {
98 self.enabled = enabled;
99 }
100
101 async fn search(
102 &self,
103 query: &str,
104 options: &SearchOptions,
105 ) -> Result<Vec<SearchResult>, SearchError> {
106 if query.trim().is_empty() {
107 return Ok(Vec::new());
108 }
109
110 let limit = options.limit.unwrap_or(web_capture::DEFAULT_LIMIT);
111
112 match web_capture::search(query, &self.engine, limit, "fetch", "").await {
113 Ok(result) => Ok(self.adapt_items(result.results)),
114 Err(message) => {
115 tracing::warn!(
116 provider = self.name(),
117 error = %message,
118 "WebCaptureProvider returned no results"
119 );
120 Ok(Vec::new())
121 }
122 }
123 }
124}