barter_data/exchange/bybit/
message.rs1use std::fmt::Debug;
2
3use crate::{Identifier, exchange::bybit::channel::BybitChannel};
4use barter_integration::subscription::SubscriptionId;
5use chrono::{DateTime, Utc};
6use serde::{
7 Deserialize, Serialize,
8 de::{Error, Unexpected},
9};
10
11#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
34pub struct BybitPayload<T> {
35 #[serde(alias = "topic", deserialize_with = "de_message_subscription_id")]
36 pub subscription_id: SubscriptionId,
37
38 #[serde(rename = "type")]
39 pub kind: BybitPayloadKind,
40
41 #[serde(
42 alias = "ts",
43 deserialize_with = "barter_integration::de::de_u64_epoch_ms_as_datetime_utc"
44 )]
45 pub time: DateTime<Utc>,
46
47 pub data: T,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
52#[serde(rename_all = "lowercase")]
53pub enum BybitPayloadKind {
54 Snapshot,
55 Delta,
56}
57
58pub fn de_message_subscription_id<'de, D>(deserializer: D) -> Result<SubscriptionId, D::Error>
63where
64 D: serde::de::Deserializer<'de>,
65{
66 let input = <&str as serde::Deserialize>::deserialize(deserializer)?;
67 let mut tokens = input.split('.');
68
69 match (tokens.next(), tokens.next(), tokens.next()) {
70 (Some("publicTrade"), Some(market), None) => Ok(SubscriptionId::from(format!(
71 "{}|{market}",
72 BybitChannel::TRADES.0
73 ))),
74 (Some("orderbook"), Some("1"), Some(market)) => Ok(SubscriptionId::from(format!(
75 "{}|{market}",
76 BybitChannel::ORDER_BOOK_L1.0,
77 ))),
78 (Some("orderbook"), Some("50"), Some(market)) => Ok(SubscriptionId::from(format!(
79 "{}|{market}",
80 BybitChannel::ORDER_BOOK_L2.0,
81 ))),
82 _ => Err(Error::invalid_value(
83 Unexpected::Str(input),
84 &"invalid message type expected pattern: <type>.<symbol>",
85 )),
86 }
87}
88
89impl<T> Identifier<Option<SubscriptionId>> for BybitPayload<T> {
90 fn id(&self) -> Option<SubscriptionId> {
91 Some(self.subscription_id.clone())
92 }
93}
94
95#[cfg(test)]
96mod tests {
97
98 mod de {
99 use crate::exchange::bybit::subscription::{BybitResponse, BybitReturnMessage};
100 use barter_integration::error::SocketError;
101
102 #[test]
103 fn test_bybit_pong() {
104 struct TestCase {
105 input: &'static str,
106 expected: Result<BybitResponse, SocketError>,
107 }
108
109 let tests = vec![
110 TestCase {
112 input: r#"
113 {
114 "success": true,
115 "ret_msg": "pong",
116 "conn_id": "0970e817-426e-429a-a679-ff7f55e0b16a",
117 "op": "ping"
118 }
119 "#,
120 expected: Ok(BybitResponse {
121 success: true,
122 ret_msg: BybitReturnMessage::Pong,
123 }),
124 },
125 ];
126
127 for (index, test) in tests.into_iter().enumerate() {
128 let actual = serde_json::from_str::<BybitResponse>(test.input);
129 match (actual, test.expected) {
130 (Ok(actual), Ok(expected)) => {
131 assert_eq!(actual, expected, "TC{} failed", index)
132 }
133 (Err(_), Err(_)) => {
134 }
136 (actual, expected) => {
137 panic!(
139 "TC{index} failed because actual != expected. \nActual: {actual:?}\nExpected: {expected:?}\n"
140 );
141 }
142 }
143 }
144 }
145 }
146}