use serde::{Deserialize, Serialize};
use crate::adapters::common::encode_path_segment;
use crate::error::Result;
use super::build_client;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DividendHistory {
pub date: Option<String>,
pub label: Option<String>,
#[serde(rename = "adjDividend")]
pub adj_dividend: Option<f64>,
pub dividend: Option<f64>,
#[serde(rename = "recordDate")]
pub record_date: Option<String>,
#[serde(rename = "paymentDate")]
pub payment_date: Option<String>,
#[serde(rename = "declarationDate")]
pub declaration_date: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DividendHistoryResponse {
pub symbol: Option<String>,
pub historical: Vec<DividendHistory>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SplitHistory {
pub date: Option<String>,
pub label: Option<String>,
pub numerator: Option<f64>,
pub denominator: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SplitHistoryResponse {
pub symbol: Option<String>,
pub historical: Vec<SplitHistory>,
}
pub async fn historical_dividends(symbol: &str) -> Result<DividendHistoryResponse> {
let client = build_client()?;
let path = format!(
"/api/v3/historical-price-full/stock_dividend/{}",
encode_path_segment(symbol)
);
client.get(&path, &[]).await
}
pub async fn historical_splits(symbol: &str) -> Result<SplitHistoryResponse> {
let client = build_client()?;
let path = format!(
"/api/v3/historical-price-full/stock_split/{}",
encode_path_segment(symbol)
);
client.get(&path, &[]).await
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_historical_dividends_mock() {
let mut server = mockito::Server::new_async().await;
let _mock = server
.mock("GET", "/api/v3/historical-price-full/stock_dividend/AAPL")
.match_query(mockito::Matcher::AllOf(vec![mockito::Matcher::UrlEncoded(
"apikey".into(),
"test-key".into(),
)]))
.with_status(200)
.with_body(
serde_json::json!({
"symbol": "AAPL",
"historical": [
{
"date": "2024-02-09",
"label": "February 09, 24",
"adjDividend": 0.24,
"dividend": 0.24,
"recordDate": "2024-02-12",
"paymentDate": "2024-02-15",
"declarationDate": "2024-02-01"
}
]
})
.to_string(),
)
.create_async()
.await;
let client = super::super::build_test_client(&server.url()).unwrap();
let path = "/api/v3/historical-price-full/stock_dividend/AAPL";
let resp: DividendHistoryResponse = client.get(path, &[]).await.unwrap();
assert_eq!(resp.symbol.as_deref(), Some("AAPL"));
assert_eq!(resp.historical.len(), 1);
assert!((resp.historical[0].adj_dividend.unwrap() - 0.24).abs() < 0.001);
}
#[tokio::test]
async fn test_historical_splits_mock() {
let mut server = mockito::Server::new_async().await;
let _mock = server
.mock("GET", "/api/v3/historical-price-full/stock_split/AAPL")
.match_query(mockito::Matcher::AllOf(vec![mockito::Matcher::UrlEncoded(
"apikey".into(),
"test-key".into(),
)]))
.with_status(200)
.with_body(
serde_json::json!({
"symbol": "AAPL",
"historical": [
{
"date": "2020-08-31",
"label": "August 31, 20",
"numerator": 4.0,
"denominator": 1.0
}
]
})
.to_string(),
)
.create_async()
.await;
let client = super::super::build_test_client(&server.url()).unwrap();
let path = "/api/v3/historical-price-full/stock_split/AAPL";
let resp: SplitHistoryResponse = client.get(path, &[]).await.unwrap();
assert_eq!(resp.symbol.as_deref(), Some("AAPL"));
assert_eq!(resp.historical.len(), 1);
assert!((resp.historical[0].numerator.unwrap() - 4.0).abs() < 0.001);
}
}