use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)]
pub struct OrderBookLevel {
#[serde(rename = "0")]
pub price: String,
#[serde(rename = "1")]
pub size: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct OrderBookData {
#[serde(rename = "s")]
pub symbol: String,
#[serde(rename = "b")]
pub bids: Vec<OrderBookLevel>,
#[serde(rename = "a")]
pub asks: Vec<OrderBookLevel>,
#[serde(rename = "u")]
pub update_id: u64,
#[serde(rename = "seq")]
#[serde(default)]
pub seq: Option<u64>,
#[serde(rename = "cts")]
#[serde(default)]
pub cts: Option<u64>,
#[serde(rename = "type")]
pub msg_type: String,
}
pub struct OrderBookStream;
impl OrderBookStream {
pub fn parse(data: &serde_json::Value) -> serde_json::Result<OrderBookData> {
serde_json::from_value(data.clone())
}
pub fn matches_topic(topic: &str) -> bool {
topic.starts_with("orderbook.")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_snapshot() {
let json = serde_json::json!({
"s": "BTCUSDT",
"b": [["50000.00", "1.5"], ["49900.00", "2.0"]],
"a": [["50100.00", "1.0"], ["50200.00", "0.5"]],
"u": 1,
"seq": 100,
"type": "snapshot"
});
let data: OrderBookData = serde_json::from_value(json).unwrap();
assert_eq!(data.symbol, "BTCUSDT");
assert_eq!(data.bids.len(), 2);
assert_eq!(data.asks.len(), 2);
assert_eq!(data.bids[0].price, "50000.00");
assert_eq!(data.bids[0].size, "1.5");
assert_eq!(data.msg_type, "snapshot");
}
#[test]
fn test_parse_delta() {
let json = serde_json::json!({
"s": "ETHUSDT",
"b": [],
"a": [["3000.00", "0.1"]],
"u": 105,
"seq": 105,
"type": "delta"
});
let data: OrderBookData = serde_json::from_value(json).unwrap();
assert_eq!(data.symbol, "ETHUSDT");
assert_eq!(data.asks.len(), 1);
assert_eq!(data.msg_type, "delta");
}
#[test]
fn test_matches_topic() {
assert!(OrderBookStream::matches_topic("orderbook.1.BTCUSDT"));
assert!(OrderBookStream::matches_topic("orderbook.50.ETHUSDT"));
assert!(!OrderBookStream::matches_topic("publicTrade.BTCUSDT"));
}
}