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