use std::sync::Arc;
use std::time::{Duration, Instant};
use serde::{Deserialize, Serialize};
use crate::error::WebSocketError;
use crate::models::{ChangeVec, Orderbook};
use crate::websocket::traits::{ActivityFill, ActivityTrade};
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(tag = "kind")]
pub enum WsUpdate {
Snapshot {
market_id: String,
book: Arc<Orderbook>,
exchange_ts: Option<u64>,
#[serde(skip)]
#[cfg_attr(feature = "schema", schemars(skip))]
local_ts: Instant,
local_ts_ms: u64,
seq: u64,
},
Delta {
market_id: String,
changes: ChangeVec,
exchange_ts: Option<u64>,
#[serde(skip)]
#[cfg_attr(feature = "schema", schemars(skip))]
local_ts: Instant,
local_ts_ms: u64,
seq: u64,
},
Trade {
trade: ActivityTrade,
#[serde(skip)]
#[cfg_attr(feature = "schema", schemars(skip))]
local_ts: Instant,
local_ts_ms: u64,
},
Fill {
fill: ActivityFill,
#[serde(skip)]
#[cfg_attr(feature = "schema", schemars(skip))]
local_ts: Instant,
local_ts_ms: u64,
},
}
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(tag = "kind")]
pub enum SessionEvent {
Connected,
Reconnected { gap_ms: u64 },
Lagged {
dropped: u64,
first_seq: u64,
last_seq: u64,
},
BookInvalidated {
market_id: String,
reason: InvalidationReason,
},
Error { message: String },
}
impl SessionEvent {
#[inline]
pub fn reconnected(gap: Duration) -> Self {
Self::Reconnected {
gap_ms: u64::try_from(gap.as_millis()).unwrap_or(u64::MAX),
}
}
#[inline]
pub fn error(err: WebSocketError) -> Self {
Self::Error {
message: err.to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum InvalidationReason {
Reconnect,
Lag,
SequenceGap { expected: u64, received: u64 },
ExchangeReset,
}
impl WsUpdate {
#[inline]
pub fn local_ts(&self) -> Instant {
match self {
Self::Snapshot { local_ts, .. }
| Self::Delta { local_ts, .. }
| Self::Trade { local_ts, .. }
| Self::Fill { local_ts, .. } => *local_ts,
}
}
#[inline]
pub fn local_ts_ms(&self) -> u64 {
match self {
Self::Snapshot { local_ts_ms, .. }
| Self::Delta { local_ts_ms, .. }
| Self::Trade { local_ts_ms, .. }
| Self::Fill { local_ts_ms, .. } => *local_ts_ms,
}
}
#[inline]
pub fn market_id(&self) -> Option<&str> {
match self {
Self::Snapshot { market_id, .. } | Self::Delta { market_id, .. } => Some(market_id),
Self::Trade { trade, .. } => Some(&trade.market_id),
Self::Fill { fill, .. } => Some(&fill.market_id),
}
}
}
#[inline]
pub fn now_pair() -> (Instant, u64) {
let local_ts = Instant::now();
let local_ts_ms = chrono::Utc::now().timestamp_millis() as u64;
(local_ts, local_ts_ms)
}