use once_cell::sync::OnceCell;
use std::sync::{mpsc, Mutex};
use std::thread;
use std::fmt;
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SpotEvent {
SpotNewMarketPrice {
client_ids: Vec<Vec<u8>>,
#[serde(with = "serde_bytes")]
pair_id: Vec<u8>,
#[serde(with = "serde_bytes")]
base_asset_id: Vec<u8>,
#[serde(with = "serde_bytes")]
quote_asset_id: Vec<u8>,
price: u64,
timestamp: i64,
},
SpotPairRebate {
#[serde(with = "serde_bytes")]
pair_id: Vec<u8>,
#[serde(with = "serde_bytes")]
base_asset_id: Vec<u8>,
#[serde(with = "serde_bytes")]
quote_asset_id: Vec<u8>,
price: u64,
is_bid: bool,
#[serde(with = "serde_bytes")]
account_id: Vec<u8>,
is_maker: bool,
fee_bps: i16,
base_volume: u64,
quote_volume: u64,
timestamp: i64,
},
SpotPairClientAccountChanged {
#[serde(with = "serde_bytes")]
pair_id: Vec<u8>,
#[serde(with = "serde_bytes")]
cid: Option<Vec<u8>>,
#[serde(with = "serde_bytes")]
admin_account_id: Option<Vec<u8>>,
#[serde(with = "serde_bytes")]
fee_account_id: Option<Vec<u8>>,
timestamp: i64,
},
SpotPairAdded {
#[serde(with = "serde_bytes")]
cid: Vec<u8>,
#[serde(with = "serde_bytes")]
pair_id: Vec<u8>,
timestamp: i64,
},
Transfer {
#[serde(with = "serde_bytes")]
cid: Vec<u8>,
#[serde(with = "serde_bytes")]
from: Vec<u8>,
#[serde(with = "serde_bytes")]
to: Vec<u8>,
#[serde(with = "serde_bytes")]
asset: Vec<u8>,
amnt: u64,
timestamp: i64,
},
SpotPriceLevelChanged {
#[serde(with = "serde_bytes")]
pair_id: Vec<u8>,
is_bid: bool,
price: u64,
pqty: u64,
cqty: u64,
timestamp: i64,
},
SpotOrderPlaced {
#[serde(with = "serde_bytes")]
cid: Vec<u8>,
#[serde(with = "serde_bytes")]
pair_id: Vec<u8>,
#[serde(with = "serde_bytes")]
base_asset_id: Vec<u8>,
#[serde(with = "serde_bytes")]
quote_asset_id: Vec<u8>,
#[serde(with = "serde_bytes")]
order_id: Vec<u8>,
#[serde(with = "serde_bytes")]
maker_account_id: Vec<u8>,
is_bid: bool,
price: u64,
amnt: u64,
iqty: u64,
cqty: u64,
pqty: u64,
timestamp: i64,
expires_at: i64
},
SpotOrderPartiallyFilled {
is_taker_event: bool,
#[serde(with = "serde_bytes")]
taker_cid: Vec<u8>,
#[serde(with = "serde_bytes")]
maker_cid: Vec<u8>,
#[serde(with = "serde_bytes")]
taker_order_id: Vec<u8>,
#[serde(with = "serde_bytes")]
maker_order_id: Vec<u8>,
#[serde(with = "serde_bytes")]
taker_account_id: Vec<u8>,
#[serde(with = "serde_bytes")]
maker_account_id: Vec<u8>,
taker_order_is_bid: bool,
maker_order_is_bid: bool,
price: u64,
#[serde(with = "serde_bytes")]
pair_id: Vec<u8>,
#[serde(with = "serde_bytes")]
base_asset_id: Vec<u8>,
#[serde(with = "serde_bytes")]
quote_asset_id: Vec<u8>,
base_volume: u64,
quote_volume: u64,
base_fee: u64,
quote_fee: u64,
maker_fee_bps: i16,
taker_fee_bps: i16,
amnt: u64,
iqty: u64,
pqty: u64,
cqty: u64,
timestamp: i64,
expires_at: i64
},
SpotOrderFullyFilled {
is_taker_event: bool,
#[serde(with = "serde_bytes")]
taker_cid: Vec<u8>,
#[serde(with = "serde_bytes")]
maker_cid: Vec<u8>,
#[serde(with = "serde_bytes")]
taker_order_id: Vec<u8>,
#[serde(with = "serde_bytes")]
maker_order_id: Vec<u8>,
#[serde(with = "serde_bytes")]
maker_account_id: Vec<u8>,
#[serde(with = "serde_bytes")]
taker_account_id: Vec<u8>,
taker_order_is_bid: bool,
maker_order_is_bid: bool,
price: u64,
#[serde(with = "serde_bytes")]
pair_id: Vec<u8>,
#[serde(with = "serde_bytes")]
base_asset_id: Vec<u8>,
#[serde(with = "serde_bytes")]
quote_asset_id: Vec<u8>,
base_volume: u64,
quote_volume: u64,
base_fee: u64,
quote_fee: u64,
maker_fee_bps: i16,
taker_fee_bps: i16,
amnt: u64,
iqty: u64,
pqty: u64,
cqty: u64,
timestamp: i64,
expires_at: i64
},
SpotOrderCancelled {
#[serde(with = "serde_bytes")]
cid: Vec<u8>,
#[serde(with = "serde_bytes")]
order_id: Vec<u8>,
#[serde(with = "serde_bytes")]
maker_account_id: Vec<u8>,
is_bid: bool,
price: u64,
amnt: u64,
iqty: u64,
pqty: u64,
cqty: u64,
timestamp: i64,
expires_at: i64
},
SpotOrderExpired {
#[serde(with = "serde_bytes")]
cid: Vec<u8>,
#[serde(with = "serde_bytes")]
order_id: Vec<u8>,
#[serde(with = "serde_bytes")]
maker_account_id: Vec<u8>,
is_bid: bool,
price: u64,
amnt: u64,
iqty: u64,
pqty: u64,
cqty: u64,
timestamp: i64,
expires_at: i64
},
SpotOrderIcebergQuantityChanged {
#[serde(with = "serde_bytes")]
cid: Vec<u8>,
#[serde(with = "serde_bytes")]
order_id: Vec<u8>,
amnt: u64,
iqty: u64,
pqty: u64,
cqty: u64,
timestamp: i64,
expires_at: i64
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct EventQueue(pub Vec<SpotEvent>);
impl EventQueue {
pub fn new() -> Self {
EventQueue(Vec::new())
}
pub fn from_vec(events: Vec<SpotEvent>) -> Self {
EventQueue(events)
}
pub fn as_vec(&self) -> &Vec<SpotEvent> {
&self.0
}
pub fn into_vec(self) -> Vec<SpotEvent> {
self.0
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn log(&self, prefix: &str) {
if self.0.is_empty() {
println!("{prefix}[]");
return;
}
for (i, event) in self.0.iter().enumerate() {
println!("{prefix}[{i}]: {:?}", event);
}
}
}
impl Default for EventQueue {
fn default() -> Self {
EventQueue::new()
}
}
impl From<Vec<SpotEvent>> for EventQueue {
fn from(events: Vec<SpotEvent>) -> Self {
EventQueue(events)
}
}
impl From<EventQueue> for Vec<SpotEvent> {
fn from(queue: EventQueue) -> Self {
queue.0
}
}
impl fmt::Display for EventQueue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.is_empty() {
return write!(f, "[]");
}
write!(f, "[")?;
for (i, event) in self.0.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{:?}", event)?;
}
write!(f, "]")
}
}
impl std::ops::Deref for EventQueue {
type Target = Vec<SpotEvent>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for EventQueue {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
pub trait EventBackend: Send + 'static {
fn handle_event(&mut self, event: SpotEvent);
}
static DISPATCH_TX: OnceCell<mpsc::Sender<SpotEvent>> = OnceCell::new();
static BACKEND_TXS: OnceCell<Mutex<Vec<mpsc::Sender<SpotEvent>>>> = OnceCell::new();
static EVENT_QUEUE: OnceCell<Mutex<Vec<SpotEvent>>> = OnceCell::new();
fn backend_txs() -> &'static Mutex<Vec<mpsc::Sender<SpotEvent>>> {
BACKEND_TXS.get_or_init(|| Mutex::new(Vec::new()))
}
fn event_queue() -> &'static Mutex<Vec<SpotEvent>> {
EVENT_QUEUE.get_or_init(|| Mutex::new(Vec::new()))
}
pub fn init_event_bus() {
let (tx, rx) = mpsc::channel::<SpotEvent>();
DISPATCH_TX.set(tx).ok();
thread::spawn(move || {
while let Ok(event) = rx.recv() {
let backends = backend_txs().lock().unwrap();
for backend_tx in backends.iter() {
let _ = backend_tx.send(event.clone());
}
}
});
}
pub fn emit_event(event: SpotEvent) {
let mut queue = event_queue().lock().unwrap();
queue.push(event);
}
pub fn publish_events() {
let events: Vec<SpotEvent> = {
let mut queue = event_queue().lock().unwrap();
let drained = queue.clone();
queue.clear();
drained
};
if let Some(tx) = DISPATCH_TX.get() {
for event in events {
let _ = tx.send(event);
}
}
}
pub fn publish_event_queue(events: EventQueue) {
if let Some(tx) = DISPATCH_TX.get() {
for event in events.into_vec() {
let _ = tx.send(event);
}
}
}
pub fn register_backend() -> mpsc::Receiver<SpotEvent> {
let (tx, rx) = mpsc::channel::<SpotEvent>();
{
let mut list = backend_txs().lock().unwrap();
list.push(tx);
}
rx
}
pub fn drain_events() -> EventQueue {
let mut queue = event_queue().lock().unwrap();
let drained = queue.clone();
queue.clear();
EventQueue(drained)
}
pub fn clear_events() {
let mut queue = event_queue().lock().unwrap();
queue.clear();
}