use serde::{Deserialize, Serialize};
use crate::adapters::common::encode_path_segment;
use crate::error::{FinanceError, Result};
use super::super::build_client;
use super::super::models::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CryptoLastTrade {
pub price: Option<f64>,
pub size: Option<f64>,
pub exchange: Option<i32>,
pub conditions: Option<Vec<i32>>,
pub timestamp: Option<i64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CryptoLastTradeResponse {
pub last: Option<CryptoLastTrade>,
pub request_id: Option<String>,
pub status: Option<String>,
pub symbol: Option<String>,
}
pub async fn crypto_last_trade(from: &str, to: &str) -> Result<CryptoLastTradeResponse> {
let client = build_client()?;
let path = format!(
"/v1/last/crypto/{}/{}",
encode_path_segment(from),
encode_path_segment(to)
);
let json = client.get_raw(&path, &[]).await?;
serde_json::from_value(json).map_err(|e| FinanceError::ResponseStructureError {
field: "crypto_last_trade".to_string(),
context: format!("Failed to parse crypto last trade response: {e}"),
})
}
pub async fn crypto_trades(
ticker: &str,
params: &[(&str, &str)],
) -> Result<PaginatedResponse<Trade>> {
let client = build_client()?;
let path = format!("/v3/trades/{}", encode_path_segment(ticker));
client.get(&path, params).await
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_crypto_last_trade_mock() {
let mut server = mockito::Server::new_async().await;
let _mock = server
.mock("GET", "/v1/last/crypto/BTC/USD")
.match_query(mockito::Matcher::AllOf(vec![mockito::Matcher::UrlEncoded(
"apiKey".into(),
"test-key".into(),
)]))
.with_status(200)
.with_header("content-type", "application/json")
.with_body(
serde_json::json!({
"request_id": "abc123",
"status": "OK",
"symbol": "X:BTCUSD",
"last": {
"price": 43100.50,
"size": 0.5,
"exchange": 2,
"conditions": [1],
"timestamp": 1705363200000_i64
}
})
.to_string(),
)
.create_async()
.await;
let client = super::super::super::build_test_client(&server.url()).unwrap();
let json = client
.get_raw("/v1/last/crypto/BTC/USD", &[])
.await
.unwrap();
let resp: CryptoLastTradeResponse = serde_json::from_value(json).unwrap();
assert_eq!(resp.status.as_deref(), Some("OK"));
assert_eq!(resp.symbol.as_deref(), Some("X:BTCUSD"));
let last = resp.last.unwrap();
assert!((last.price.unwrap() - 43100.50).abs() < 0.01);
assert!((last.size.unwrap() - 0.5).abs() < 0.01);
}
}