use std::sync::atomic::{AtomicU8, Ordering};
use bitflags::bitflags;
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MessageInterest: u8 {
const NONE = 0;
const BOOK = 1;
const PRICE_CHANGE = 1 << 1;
const TICK_SIZE = 1 << 2;
const LAST_TRADE_PRICE = 1 << 3;
const TRADE = 1 << 4;
const ORDER = 1 << 5;
const MARKET = Self::BOOK.bits()
| Self::PRICE_CHANGE.bits()
| Self::TICK_SIZE.bits()
| Self::LAST_TRADE_PRICE.bits();
const USER = Self::TRADE.bits() | Self::ORDER.bits();
const ALL = Self::MARKET.bits() | Self::USER.bits();
}
}
impl MessageInterest {
#[must_use]
pub fn from_event_type(event_type: &str) -> Self {
match event_type {
"book" => Self::BOOK,
"price_change" => Self::PRICE_CHANGE,
"tick_size_change" => Self::TICK_SIZE,
"last_trade_price" => Self::LAST_TRADE_PRICE,
"trade" => Self::TRADE,
"order" => Self::ORDER,
_ => Self::NONE,
}
}
#[must_use]
pub fn is_interested_in_event(&self, event_type: &str) -> bool {
let interest = MessageInterest::from_event_type(event_type);
!interest.is_empty() && self.contains(interest)
}
}
impl Default for MessageInterest {
fn default() -> Self {
Self::ALL
}
}
#[derive(Debug, Default)]
pub struct InterestTracker {
interest: AtomicU8,
}
impl InterestTracker {
#[must_use]
pub const fn new() -> Self {
Self {
interest: AtomicU8::new(0),
}
}
pub fn add(&self, interest: MessageInterest) {
self.interest.fetch_or(interest.bits(), Ordering::Release);
}
#[must_use]
pub fn get(&self) -> MessageInterest {
MessageInterest::from_bits(self.interest.load(Ordering::Acquire))
.unwrap_or(MessageInterest::NONE)
}
#[must_use]
pub fn is_interested(&self, interest: MessageInterest) -> bool {
self.get().contains(interest)
}
#[must_use]
pub fn is_interested_in_event(&self, event_type: &str) -> bool {
let interest = MessageInterest::from_event_type(event_type);
!interest.is_empty() && self.is_interested(interest)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn interest_contains() {
assert!(MessageInterest::MARKET.contains(MessageInterest::BOOK));
assert!(MessageInterest::MARKET.contains(MessageInterest::PRICE_CHANGE));
assert!(!MessageInterest::MARKET.contains(MessageInterest::TRADE));
assert!(MessageInterest::ALL.contains(MessageInterest::TRADE));
}
#[test]
fn interest_from_event_type() {
assert_eq!(
MessageInterest::from_event_type("book"),
MessageInterest::BOOK
);
assert_eq!(
MessageInterest::from_event_type("trade"),
MessageInterest::TRADE
);
assert_eq!(
MessageInterest::from_event_type("unknown"),
MessageInterest::NONE
);
}
#[test]
fn tracker_add_and_get() {
let tracker = InterestTracker::new();
assert!(tracker.get().is_empty());
tracker.add(MessageInterest::BOOK);
assert!(tracker.is_interested(MessageInterest::BOOK));
assert!(!tracker.is_interested(MessageInterest::TRADE));
tracker.add(MessageInterest::TRADE);
assert!(tracker.is_interested(MessageInterest::BOOK));
assert!(tracker.is_interested(MessageInterest::TRADE));
}
}