use serde::{Deserialize, Serialize};
fn deserialize_number<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{self, Visitor};
struct NumberVisitor;
impl<'de> Visitor<'de> for NumberVisitor {
type Value = f64;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a number or string representation of a number")
}
fn visit_f64<E>(self, value: f64) -> Result<f64, E> {
Ok(value)
}
fn visit_i64<E>(self, value: i64) -> Result<f64, E> {
Ok(value as f64)
}
fn visit_u64<E>(self, value: u64) -> Result<f64, E> {
Ok(value as f64)
}
fn visit_str<E>(self, value: &str) -> Result<f64, E>
where
E: de::Error,
{
value.parse::<f64>().map_err(de::Error::custom)
}
}
deserializer.deserialize_any(NumberVisitor)
}
fn deserialize_optional_number<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{self, Visitor};
struct OptionalNumberVisitor;
impl<'de> Visitor<'de> for OptionalNumberVisitor {
type Value = Option<f64>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a number, string representation of a number, or null")
}
fn visit_none<E>(self) -> Result<Option<f64>, E> {
Ok(None)
}
fn visit_unit<E>(self) -> Result<Option<f64>, E> {
Ok(None)
}
fn visit_f64<E>(self, value: f64) -> Result<Option<f64>, E> {
Ok(Some(value))
}
fn visit_i64<E>(self, value: i64) -> Result<Option<f64>, E> {
Ok(Some(value as f64))
}
fn visit_u64<E>(self, value: u64) -> Result<Option<f64>, E> {
Ok(Some(value as f64))
}
fn visit_str<E>(self, value: &str) -> Result<Option<f64>, E>
where
E: de::Error,
{
value.parse::<f64>().map(Some).map_err(de::Error::custom)
}
}
deserializer.deserialize_any(OptionalNumberVisitor)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Ticker {
pub symbol: String,
pub bid: f64,
pub bid_qty: f64,
pub ask: f64,
pub ask_qty: f64,
pub last: f64,
pub volume: f64,
pub vwap: f64,
pub low: f64,
pub high: f64,
pub change: f64,
pub change_pct: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TickerDataRaw {
pub symbol: String,
#[serde(deserialize_with = "deserialize_number")]
pub bid: f64,
#[serde(deserialize_with = "deserialize_number")]
pub bid_qty: f64,
#[serde(deserialize_with = "deserialize_number")]
pub ask: f64,
#[serde(deserialize_with = "deserialize_number")]
pub ask_qty: f64,
#[serde(deserialize_with = "deserialize_number")]
pub last: f64,
#[serde(deserialize_with = "deserialize_number")]
pub volume: f64,
#[serde(deserialize_with = "deserialize_number")]
pub vwap: f64,
#[serde(deserialize_with = "deserialize_number")]
pub low: f64,
#[serde(deserialize_with = "deserialize_number")]
pub high: f64,
#[serde(deserialize_with = "deserialize_number")]
pub change: f64,
#[serde(deserialize_with = "deserialize_number")]
pub change_pct: f64,
#[serde(default, deserialize_with = "deserialize_optional_number")]
pub volume_usd: Option<f64>,
#[serde(default)]
pub timestamp: String,
}
impl TickerDataRaw {
pub fn to_ticker(&self) -> Ticker {
Ticker {
symbol: self.symbol.clone(),
bid: self.bid,
bid_qty: self.bid_qty,
ask: self.ask,
ask_qty: self.ask_qty,
last: self.last,
volume: self.volume,
vwap: self.vwap,
low: self.low,
high: self.high,
change: self.change,
change_pct: self.change_pct,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TickerUpdate {
#[serde(default)]
pub channel: String,
#[serde(rename = "type")]
pub update_type: String,
pub data: Vec<TickerDataRaw>,
}