Skip to main content

barter_data/exchange/bitmex/
subscription.rs

1use barter_integration::{Validator, error::SocketError};
2use serde::{Deserialize, Serialize};
3
4/// ### Raw Payload Examples
5/// See docs: <https://www.bitmex.com/app/wsAPI#Response-Format>
6/// #### Subscription response payload
7/// ```json
8/// {
9///     "success": true,
10///     "subscribe": "trade:XBTUSD",
11///     "request": {
12///         "op":"subscribe",
13///         "args":[
14///             "trade:XBTUSD"
15///         ]
16///     }
17/// }
18///```
19#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
20pub struct BitmexSubResponse {
21    success: bool,
22    subscribe: String,
23}
24
25impl Validator for BitmexSubResponse {
26    type Error = SocketError;
27
28    fn validate(self) -> Result<Self, Self::Error>
29    where
30        Self: Sized,
31    {
32        if self.success {
33            Ok(self)
34        } else {
35            Err(SocketError::Subscribe(format!(
36                "received failure subscription response for {} subscription",
37                self.subscribe
38            )))
39        }
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    mod de {
48        use super::*;
49
50        #[test]
51        fn test_bitmex_sub_response() {
52            struct TestCase {
53                input: &'static str,
54                expected: Result<BitmexSubResponse, SocketError>,
55            }
56
57            let cases = vec![
58                TestCase {
59                    // TC0: input response is Subscribed
60                    input: r#"
61                        {
62                            "success": true,
63                            "subscribe": "orderBookL2_25:XBTUSD",
64                            "request": {
65                                "op":"subscribe",
66                                "args":[
67                                    "orderBookL2_25:XBTUSD"
68                                ]
69                            }
70                        }
71                    "#,
72                    expected: Ok(BitmexSubResponse {
73                        success: true,
74                        subscribe: "orderBookL2_25:XBTUSD".to_string(),
75                    }),
76                },
77                TestCase {
78                    // TC1: input response is failed subscription
79                    input: r#"
80                    {
81                        "success": false,
82                        "subscribe": "orderBookL2_25:XBTUSD"
83                    }
84                    "#,
85                    expected: Ok(BitmexSubResponse {
86                        success: false,
87                        subscribe: "orderBookL2_25:XBTUSD".to_string(),
88                    }),
89                },
90            ];
91
92            for (index, test) in cases.into_iter().enumerate() {
93                let actual = serde_json::from_str::<BitmexSubResponse>(test.input);
94                match (actual, test.expected) {
95                    (Ok(actual), Ok(expected)) => {
96                        assert_eq!(actual, expected, "TC{} failed", index)
97                    }
98                    (Err(_), Err(_)) => {
99                        // Test passed
100                    }
101                    (actual, expected) => {
102                        // Test failed
103                        panic!(
104                            "TC{index} failed because actual != expected. \nActual: {actual:?}\nExpected: {expected:?}\n"
105                        );
106                    }
107                }
108            }
109        }
110    }
111
112    #[test]
113    fn test_validate_bitmex_sub_response() {
114        struct TestCase {
115            input_response: BitmexSubResponse,
116            is_valid: bool,
117        }
118
119        let cases = vec![
120            TestCase {
121                // TC0: input response is successful subscription
122                input_response: BitmexSubResponse {
123                    success: true,
124                    subscribe: "orderBookL2_25:XBTUSD".to_string(),
125                },
126                is_valid: true,
127            },
128            TestCase {
129                // TC1: input response is failed subscription
130                input_response: BitmexSubResponse {
131                    success: false,
132                    subscribe: "orderBookL2_25:XBTUSD".to_string(),
133                },
134                is_valid: false,
135            },
136        ];
137
138        for (index, test) in cases.into_iter().enumerate() {
139            let actual = test.input_response.validate().is_ok();
140            assert_eq!(actual, test.is_valid, "TestCase {} failed", index);
141        }
142    }
143}