use serde::Deserialize;
use serde::de::{self, SeqAccess, Visitor};
use std::fmt;
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Ticker24hr {
pub symbol: String,
pub price_change: String,
pub price_change_percent: String,
pub last_price: String,
pub high_price: String,
pub low_price: String,
pub quote_volume: String,
}
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct BinanceError {
#[serde(default)]
pub msg: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct Kline {
pub open_time: i64,
pub open: f64,
pub high: f64,
pub low: f64,
pub close: f64,
pub volume: f64,
}
impl<'de> Deserialize<'de> for Kline {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct KlineVisitor;
impl<'de> Visitor<'de> for KlineVisitor {
type Value = Kline;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a Binance kline array")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Kline, A::Error> {
fn next_num<'de, A: SeqAccess<'de>>(
seq: &mut A,
field: &'static str,
) -> Result<f64, A::Error> {
let raw: String = seq
.next_element()?
.ok_or_else(|| de::Error::missing_field(field))?;
raw.parse().map_err(|_| {
de::Error::invalid_value(de::Unexpected::Str(&raw), &"a decimal string")
})
}
let open_time: i64 = seq
.next_element()?
.ok_or_else(|| de::Error::missing_field("open_time"))?;
let open = next_num(&mut seq, "open")?;
let high = next_num(&mut seq, "high")?;
let low = next_num(&mut seq, "low")?;
let close = next_num(&mut seq, "close")?;
let volume = next_num(&mut seq, "volume")?;
while seq.next_element::<de::IgnoredAny>()?.is_some() {}
Ok(Kline {
open_time,
open,
high,
low,
close,
volume,
})
}
}
deserializer.deserialize_seq(KlineVisitor)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn kline_parses_and_ignores_the_variable_tail() {
let raw = r#"[1785628800000,"62823.65000000","63796.33000000","62806.58000000",
"63570.00000000","8387.01154000",1785715199999,"531238376.42127370",1286064,
"4566.33112000","289282425.13673770","0"]"#;
let k: Kline = serde_json::from_str(raw).unwrap();
assert_eq!(k.open_time, 1785628800000);
assert_eq!(k.open, 62823.65);
assert_eq!(k.high, 63796.33);
assert_eq!(k.low, 62806.58);
assert_eq!(k.close, 63570.0);
assert_eq!(k.volume, 8387.01154);
}
#[test]
fn kline_tolerates_a_numeric_tail_element() {
let raw = r#"[1,"1.0","2.0","0.5","1.5","10.0",2,"3.0",4,"5.0","6.0",0]"#;
assert!(serde_json::from_str::<Kline>(raw).is_ok());
}
#[test]
fn kline_rejects_a_truncated_array() {
let raw = r#"[1,"1.0","2.0"]"#;
assert!(serde_json::from_str::<Kline>(raw).is_err());
}
}