use serde::{Deserialize, Serialize};
use crate::error::Result;
use super::build_client;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SectorPe {
pub date: Option<String>,
pub sector: Option<String>,
pub exchange: Option<String>,
pub pe: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct IndustryPe {
pub date: Option<String>,
pub industry: Option<String>,
pub exchange: Option<String>,
pub pe: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SectorPerformance {
pub sector: Option<String>,
#[serde(rename = "changesPercentage")]
pub changes_percentage: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct HistoricalSectorPerformance {
pub date: Option<String>,
#[serde(rename = "utilitiesChangesPercentage")]
pub utilities_changes_percentage: Option<f64>,
#[serde(rename = "basicMaterialsChangesPercentage")]
pub basic_materials_changes_percentage: Option<f64>,
#[serde(rename = "communicationServicesChangesPercentage")]
pub communication_services_changes_percentage: Option<f64>,
#[serde(rename = "consumerCyclicalChangesPercentage")]
pub consumer_cyclical_changes_percentage: Option<f64>,
#[serde(rename = "consumerDefensiveChangesPercentage")]
pub consumer_defensive_changes_percentage: Option<f64>,
#[serde(rename = "energyChangesPercentage")]
pub energy_changes_percentage: Option<f64>,
#[serde(rename = "financialServicesChangesPercentage")]
pub financial_services_changes_percentage: Option<f64>,
#[serde(rename = "healthcareChangesPercentage")]
pub healthcare_changes_percentage: Option<f64>,
#[serde(rename = "industrialsChangesPercentage")]
pub industrials_changes_percentage: Option<f64>,
#[serde(rename = "realEstateChangesPercentage")]
pub real_estate_changes_percentage: Option<f64>,
#[serde(rename = "technologyChangesPercentage")]
pub technology_changes_percentage: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct MarketMover {
pub symbol: Option<String>,
pub name: Option<String>,
pub change: Option<f64>,
pub price: Option<f64>,
#[serde(rename = "changesPercentage")]
pub changes_percentage: Option<f64>,
}
pub async fn sectors_pe() -> Result<Vec<SectorPe>> {
let client = build_client()?;
client.get("/api/v4/sector_price_earning_ratio", &[]).await
}
pub async fn industries_pe() -> Result<Vec<IndustryPe>> {
let client = build_client()?;
client
.get("/api/v4/industry_price_earning_ratio", &[])
.await
}
pub async fn sector_performance() -> Result<Vec<SectorPerformance>> {
let client = build_client()?;
client.get("/api/v3/sector-performance", &[]).await
}
pub async fn historical_sector_performance(limit: u32) -> Result<Vec<HistoricalSectorPerformance>> {
let client = build_client()?;
let limit_str = limit.to_string();
client
.get(
"/api/v3/historical-sectors-performance",
&[("limit", &*limit_str)],
)
.await
}
pub async fn stock_market_gainers() -> Result<Vec<MarketMover>> {
let client = build_client()?;
client.get("/api/v3/stock_market/gainers", &[]).await
}
pub async fn stock_market_losers() -> Result<Vec<MarketMover>> {
let client = build_client()?;
client.get("/api/v3/stock_market/losers", &[]).await
}
pub async fn stock_market_most_active() -> Result<Vec<MarketMover>> {
let client = build_client()?;
client.get("/api/v3/stock_market/actives", &[]).await
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_sector_performance_mock() {
let mut server = mockito::Server::new_async().await;
let _mock = server
.mock("GET", "/api/v3/sector-performance")
.match_query(mockito::Matcher::AllOf(vec![mockito::Matcher::UrlEncoded(
"apikey".into(),
"test-key".into(),
)]))
.with_status(200)
.with_body(
serde_json::json!([
{
"sector": "Technology",
"changesPercentage": "1.25%"
},
{
"sector": "Healthcare",
"changesPercentage": "-0.45%"
}
])
.to_string(),
)
.create_async()
.await;
let client = super::super::build_test_client(&server.url()).unwrap();
let resp: Vec<SectorPerformance> =
client.get("/api/v3/sector-performance", &[]).await.unwrap();
assert_eq!(resp.len(), 2);
assert_eq!(resp[0].sector.as_deref(), Some("Technology"));
assert_eq!(resp[0].changes_percentage.as_deref(), Some("1.25%"));
}
#[tokio::test]
async fn test_stock_market_gainers_mock() {
let mut server = mockito::Server::new_async().await;
let _mock = server
.mock("GET", "/api/v3/stock_market/gainers")
.match_query(mockito::Matcher::AllOf(vec![mockito::Matcher::UrlEncoded(
"apikey".into(),
"test-key".into(),
)]))
.with_status(200)
.with_body(
serde_json::json!([
{
"symbol": "XYZ",
"name": "XYZ Corp",
"change": 5.20,
"price": 42.50,
"changesPercentage": 13.93
}
])
.to_string(),
)
.create_async()
.await;
let client = super::super::build_test_client(&server.url()).unwrap();
let resp: Vec<MarketMover> = client
.get("/api/v3/stock_market/gainers", &[])
.await
.unwrap();
assert_eq!(resp.len(), 1);
assert_eq!(resp[0].symbol.as_deref(), Some("XYZ"));
assert!((resp[0].change.unwrap() - 5.20).abs() < 0.01);
}
}