use std::collections::HashMap;
#[cfg(feature = "net")]
use duckduckgo::browser::Browser;
#[cfg(feature = "net")]
use duckduckgo::user_agents::get as get_ua;
#[derive(Debug, Clone)]
pub struct SearchOracle {
cache: HashMap<String, String>,
pub limit: usize,
}
impl SearchOracle {
pub fn new(limit: usize) -> Self {
Self {
cache: HashMap::new(),
limit: limit.max(1),
}
}
pub async fn fetch(&mut self, query: &str) -> String {
if let Some(cached) = self.cache.get(query) {
return cached.clone();
}
let observation = self.fetch_live(query).await;
self.cache.insert(query.to_string(), observation.clone());
observation
}
pub fn cache_len(&self) -> usize {
self.cache.len()
}
pub fn clear_cache(&mut self) {
self.cache.clear();
}
#[cfg(feature = "net")]
async fn fetch_live(&self, query: &str) -> String {
let browser = Browser::new();
let ua = get_ua("firefox").unwrap_or("Mozilla/5.0");
match browser
.lite_search(query, "wt-wt", Some(self.limit), ua)
.await
{
Ok(results) => results
.iter()
.filter_map(|r| {
let snippet = r.snippet.trim();
if !snippet.is_empty() {
Some(snippet.to_string())
} else if !r.title.trim().is_empty() {
Some(r.title.trim().to_string())
} else {
None
}
})
.collect::<Vec<_>>()
.join(" "),
Err(_) => String::new(),
}
}
#[cfg(not(feature = "net"))]
async fn fetch_live(&self, _query: &str) -> String {
String::new()
}
}