use crate::models::futopt::FutOptChannel;
use serde_json::{json, Value};
#[derive(Debug, Clone)]
pub struct FutOptSubscription {
pub channel: FutOptChannel,
pub symbol: String,
pub after_hours: bool,
}
impl Default for FutOptSubscription {
fn default() -> Self {
Self {
channel: FutOptChannel::Trades,
symbol: String::new(),
after_hours: false,
}
}
}
impl FutOptSubscription {
pub fn new(channel: FutOptChannel, symbol: impl Into<String>) -> Self {
Self {
channel,
symbol: symbol.into(),
after_hours: false,
}
}
pub fn with_after_hours(mut self, after_hours: bool) -> Self {
self.after_hours = after_hours;
self
}
pub fn key(&self) -> String {
if self.after_hours {
format!("{}:{}:afterhours", self.channel.as_str(), self.symbol)
} else {
format!("{}:{}", self.channel.as_str(), self.symbol)
}
}
pub fn to_subscribe_data(&self) -> Value {
let mut data = json!({
"channel": self.channel.as_str(),
"symbol": self.symbol,
});
if self.after_hours {
data["afterHours"] = json!(true);
}
data
}
pub fn to_subscribe_request(&self) -> Value {
json!({
"event": "subscribe",
"data": self.to_subscribe_data()
})
}
pub fn to_unsubscribe_request(id: &str) -> Value {
json!({
"event": "unsubscribe",
"data": {
"id": id
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_futopt_subscription_new() {
let sub = FutOptSubscription::new(FutOptChannel::Trades, "TXF202502");
assert_eq!(sub.channel, FutOptChannel::Trades);
assert_eq!(sub.symbol, "TXF202502");
assert!(!sub.after_hours);
}
#[test]
fn test_futopt_subscription_default() {
let sub = FutOptSubscription::default();
assert_eq!(sub.channel, FutOptChannel::Trades);
assert_eq!(sub.symbol, "");
assert!(!sub.after_hours);
}
#[test]
fn test_futopt_subscription_with_after_hours() {
let sub = FutOptSubscription::new(FutOptChannel::Trades, "TXF202502").with_after_hours(true);
assert!(sub.after_hours);
}
#[test]
fn test_futopt_subscription_key_regular() {
let sub = FutOptSubscription::new(FutOptChannel::Trades, "TXF202502");
assert_eq!(sub.key(), "trades:TXF202502");
}
#[test]
fn test_futopt_subscription_key_after_hours() {
let sub =
FutOptSubscription::new(FutOptChannel::Trades, "TXF202502").with_after_hours(true);
assert_eq!(sub.key(), "trades:TXF202502:afterhours");
}
#[test]
fn test_futopt_subscription_to_subscribe_data_regular() {
let sub = FutOptSubscription::new(FutOptChannel::Candles, "MXFB4");
let data = sub.to_subscribe_data();
assert_eq!(data["channel"], "candles");
assert_eq!(data["symbol"], "MXFB4");
assert!(data.get("afterHours").is_none());
}
#[test]
fn test_futopt_subscription_to_subscribe_data_after_hours() {
let sub = FutOptSubscription::new(FutOptChannel::Trades, "TXF202502").with_after_hours(true);
let data = sub.to_subscribe_data();
assert_eq!(data["channel"], "trades");
assert_eq!(data["symbol"], "TXF202502");
assert_eq!(data["afterHours"], true);
}
#[test]
fn test_futopt_subscription_to_subscribe_request() {
let sub = FutOptSubscription::new(FutOptChannel::Books, "TXO22000C4");
let req = sub.to_subscribe_request();
assert_eq!(req["event"], "subscribe");
assert_eq!(req["data"]["channel"], "books");
assert_eq!(req["data"]["symbol"], "TXO22000C4");
}
#[test]
fn test_futopt_subscription_to_unsubscribe_request() {
let req = FutOptSubscription::to_unsubscribe_request("futopt-sub-123");
assert_eq!(req["event"], "unsubscribe");
assert_eq!(req["data"]["id"], "futopt-sub-123");
}
#[test]
fn test_futopt_subscription_all_channels_key() {
for channel in [
FutOptChannel::Trades,
FutOptChannel::Books,
FutOptChannel::Candles,
FutOptChannel::Aggregates,
] {
let sub = FutOptSubscription::new(channel, "TXF202502");
assert!(sub.key().starts_with(channel.as_str()));
assert!(sub.key().contains("TXF202502"));
}
}
#[test]
fn test_futopt_subscription_clone() {
let sub = FutOptSubscription::new(FutOptChannel::Aggregates, "MXFC4");
let cloned = sub.clone();
assert_eq!(sub.channel, cloned.channel);
assert_eq!(sub.symbol, cloned.symbol);
assert_eq!(sub.after_hours, cloned.after_hours);
}
#[test]
fn test_futopt_subscription_string_conversion() {
let sub1 = FutOptSubscription::new(FutOptChannel::Trades, "TXF202502");
let sub2 = FutOptSubscription::new(FutOptChannel::Trades, String::from("TXF202502"));
assert_eq!(sub1.symbol, sub2.symbol);
}
}