use super::urls::api;
use crate::client::YahooClient;
use crate::constants::Region;
use crate::error::Result;
use tracing::info;
#[derive(Debug, Clone)]
pub struct SearchOptions {
pub quotes_count: u32,
pub news_count: u32,
pub enable_fuzzy_query: bool,
pub enable_logo_url: bool,
pub enable_research_reports: bool,
pub enable_cultural_assets: bool,
pub recommend_count: u32,
pub region: Option<Region>,
}
impl Default for SearchOptions {
fn default() -> Self {
Self {
quotes_count: 10,
news_count: 0,
enable_fuzzy_query: false,
enable_logo_url: true,
enable_research_reports: false,
enable_cultural_assets: false,
recommend_count: 5,
region: None,
}
}
}
impl SearchOptions {
pub fn new() -> Self {
Self::default()
}
pub fn quotes_count(mut self, count: u32) -> Self {
self.quotes_count = count;
self
}
pub fn news_count(mut self, count: u32) -> Self {
self.news_count = count;
self
}
pub fn enable_fuzzy_query(mut self, enable: bool) -> Self {
self.enable_fuzzy_query = enable;
self
}
pub fn enable_logo_url(mut self, enable: bool) -> Self {
self.enable_logo_url = enable;
self
}
pub fn enable_research_reports(mut self, enable: bool) -> Self {
self.enable_research_reports = enable;
self
}
pub fn enable_cultural_assets(mut self, enable: bool) -> Self {
self.enable_cultural_assets = enable;
self
}
pub fn recommend_count(mut self, count: u32) -> Self {
self.recommend_count = count;
self
}
pub fn region(mut self, region: Region) -> Self {
self.region = Some(region);
self
}
}
pub async fn fetch(
client: &YahooClient,
query: &str,
options: &SearchOptions,
) -> Result<serde_json::Value> {
if query.trim().is_empty() {
return Err(crate::error::FinanceError::InvalidParameter {
param: "query".to_string(),
reason: "Empty search query".to_string(),
});
}
info!("Searching for: {} (options: {:?})", query, options);
let quotes_count = options.quotes_count.to_string();
let news_count = options.news_count.to_string();
let fuzzy = options.enable_fuzzy_query.to_string();
let logo = options.enable_logo_url.to_string();
let research = options.enable_research_reports.to_string();
let cultural = options.enable_cultural_assets.to_string();
let recommend = options.recommend_count.to_string();
let lang = options
.region
.as_ref()
.map(|c| c.lang().to_string())
.unwrap_or_else(|| client.config().lang.clone());
let region = options
.region
.as_ref()
.map(|c| c.region().to_string())
.unwrap_or_else(|| client.config().region.clone());
let params = [
("q", query),
("lang", &lang),
("region", ®ion),
("quotesCount", "es_count),
("newsCount", &news_count),
("enableFuzzyQuery", &fuzzy),
("enableLogoUrl", &logo),
("enableResearchReports", &research),
("enableCulturalAssets", &cultural),
("recommendedCount", &recommend),
("listsCount", "0"), ("enableNavLinks", "false"), ("enableEnhancedTrivialQuery", "true"),
];
let response = client.request_with_params(api::SEARCH, ¶ms).await?;
Ok(response.json().await?)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::client::ClientConfig;
#[tokio::test]
#[ignore] async fn test_fetch_search() {
let client = YahooClient::new(ClientConfig::default()).await.unwrap();
let options = SearchOptions::new().quotes_count(5);
let result = fetch(&client, "Apple", &options).await;
assert!(result.is_ok());
let json = result.unwrap();
assert!(json.get("quotes").is_some());
}
#[tokio::test]
#[ignore] async fn test_fetch_search_with_news() {
let client = YahooClient::new(ClientConfig::default()).await.unwrap();
let options = SearchOptions::new()
.quotes_count(5)
.news_count(3)
.enable_research_reports(true);
let result = fetch(&client, "NVDA", &options).await;
assert!(result.is_ok());
let json = result.unwrap();
assert!(json.get("quotes").is_some());
}
#[tokio::test]
#[ignore = "requires network access - validation tested in common::tests"]
async fn test_empty_query() {
let client = YahooClient::new(ClientConfig::default()).await.unwrap();
let options = SearchOptions::new();
let result = fetch(&client, "", &options).await;
assert!(result.is_err());
}
}