Skip to main content

barter_data/exchange/bybit/
subscription.rs

1use barter_integration::{Validator, error::SocketError};
2use serde::{Deserialize, Serialize};
3
4/// [`Bybit`](super::Bybit) subscription response message.
5///
6///  ### Raw Payload Examples
7///  See docs: <https://bybit-exchange.github.io/docs/v5/ws/connect#understanding-the-subscription-response>
8///  #### Subscription Success
9/// ```json
10/// {
11///     "success": true,
12///     "ret_msg": "subscribe",
13///     "conn_id": "2324d924-aa4d-45b0-a858-7b8be29ab52b",
14///     "req_id": "10001",
15///     "op": "subscribe"
16/// }
17/// #### Subscription Failure
18/// ```json
19/// {
20///     "success": false,
21///     "ret_msg": "",
22///     "conn_id": "2324d924-aa4d-45b0-a858-7b8be29ab52b",
23///     "req_id": "10001",
24///     "op": "subscribe"
25/// }
26///
27#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
28pub struct BybitResponse {
29    pub success: bool,
30    #[serde(default)]
31    pub ret_msg: BybitReturnMessage,
32}
33
34#[derive(
35    Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize, Serialize,
36)]
37pub enum BybitReturnMessage {
38    #[serde(alias = "")]
39    #[default]
40    None,
41    #[serde(alias = "pong")]
42    Pong,
43    #[serde(alias = "subscribe")]
44    Subscribe,
45}
46
47impl Validator for BybitResponse {
48    type Error = SocketError;
49
50    fn validate(self) -> Result<Self, Self::Error>
51    where
52        Self: Sized,
53    {
54        match self.ret_msg {
55            BybitReturnMessage::None | BybitReturnMessage::Subscribe => {
56                if self.success {
57                    Ok(self)
58                } else {
59                    Err(SocketError::Subscribe(
60                        "received failure subscription response".to_owned(),
61                    ))
62                }
63            }
64            _ => Err(SocketError::Subscribe(
65                "received other message out of sequence".to_owned(),
66            )),
67        }
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    mod de {
76        use super::*;
77
78        #[test]
79        fn test_bybit_sub_response() {
80            struct TestCase {
81                input: &'static str,
82                expected: Result<BybitResponse, SocketError>,
83            }
84
85            let cases = vec![
86                TestCase {
87                    // TC0: input response is Subscribed
88                    input: r#"
89                        {
90                            "success": true,
91                            "ret_msg": "subscribe",
92                            "conn_id": "2324d924-aa4d-45b0-a858-7b8be29ab52b",
93                            "req_id": "10001",
94                            "op": "subscribe"
95                        }
96                    "#,
97                    expected: Ok(BybitResponse {
98                        success: true,
99                        ret_msg: BybitReturnMessage::Subscribe,
100                    }),
101                },
102                TestCase {
103                    // TC1: input response is failed subscription
104                    input: r#"
105                        {
106                            "success": false,
107                            "conn_id": "",
108                            "op": ""
109                        }
110                    "#,
111                    expected: Ok(BybitResponse {
112                        success: false,
113                        ret_msg: BybitReturnMessage::None,
114                    }),
115                },
116            ];
117
118            for (index, test) in cases.into_iter().enumerate() {
119                let actual = serde_json::from_str::<BybitResponse>(test.input);
120                match (actual, test.expected) {
121                    (Ok(actual), Ok(expected)) => {
122                        assert_eq!(actual, expected, "TC{} failed", index)
123                    }
124                    (Err(_), Err(_)) => {
125                        // Test passed
126                    }
127                    (actual, expected) => {
128                        // Test failed
129                        panic!(
130                            "TC{index} failed because actual != expected. \nActual: {actual:?}\nExpected: {expected:?}\n"
131                        );
132                    }
133                }
134            }
135        }
136    }
137
138    #[test]
139    fn test_validate_bybit_sub_response() {
140        struct TestCase {
141            input_response: BybitResponse,
142            is_valid: bool,
143        }
144
145        let cases = vec![
146            TestCase {
147                // TC0: input response is successful subscription
148                input_response: BybitResponse {
149                    success: true,
150                    ret_msg: BybitReturnMessage::Subscribe,
151                },
152                is_valid: true,
153            },
154            TestCase {
155                // TC1: input response is successful subscription
156                input_response: BybitResponse {
157                    success: true,
158                    ret_msg: BybitReturnMessage::None,
159                },
160                is_valid: true,
161            },
162            TestCase {
163                // TC2: input response is failed subscription
164                input_response: BybitResponse {
165                    success: false,
166                    ret_msg: BybitReturnMessage::Pong,
167                },
168                is_valid: false,
169            },
170        ];
171
172        for (index, test) in cases.into_iter().enumerate() {
173            let actual = test.input_response.validate().is_ok();
174            assert_eq!(actual, test.is_valid, "TestCase {} failed", index);
175        }
176    }
177}