Skip to main content

bybit/ws/
outgoing_message.rs

1use serde::Serialize;
2
3use crate::{SensitiveString, Timestamp, Topic, create_stream_signature, timestamp};
4
5#[derive(Serialize, Debug)]
6#[serde(tag = "op")]
7pub enum OutgoingMessage {
8    #[serde(rename = "subscribe")]
9    Subscribe {
10        #[serde(skip_serializing_if = "Option::is_none")]
11        req_id: Option<String>,
12        args: Vec<Topic>,
13    },
14    #[serde(rename = "unsubscribe")]
15    Unsubscribe {
16        #[serde(skip_serializing_if = "Option::is_none")]
17        req_id: Option<String>,
18        args: Vec<Topic>,
19    },
20    #[serde(rename = "auth")]
21    Auth {
22        #[serde(skip_serializing_if = "Option::is_none")]
23        req_id: Option<String>,
24        args: (String, Timestamp, String),
25    },
26    #[serde(rename = "ping")]
27    Ping {
28        #[serde(skip_serializing_if = "Option::is_none")]
29        req_id: Option<String>,
30    },
31    #[serde(rename = "pong")]
32    Pong {
33        #[serde(skip_serializing_if = "Option::is_none")]
34        req_id: Option<String>,
35    },
36}
37
38pub fn create_outgoing_message_auth(
39    api_key: SensitiveString,
40    api_secret: SensitiveString,
41    req_id: Option<String>,
42    recv_window: Timestamp,
43) -> OutgoingMessage {
44    let api_key = api_key.expose().to_string();
45    let expires = timestamp() + recv_window;
46
47    let signature = create_stream_signature(expires, api_secret);
48
49    OutgoingMessage::Auth {
50        req_id,
51        args: (api_key, expires, signature),
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use crate::*;
58
59    use super::*;
60
61    #[test]
62    fn test_serialize_outgoing_message_subscribe() {
63        let msg = OutgoingMessage::Subscribe {
64            req_id: Some(String::from("request_id")),
65            args: vec![
66                Topic::Ticker(String::from("BTCUSDT")),
67                Topic::Order(Category::Linear),
68            ],
69        };
70        let expected =
71            r#"{"op":"subscribe","req_id":"request_id","args":["tickers.BTCUSDT","order.linear"]}"#;
72        let serialized = serde_json::to_string(&msg).unwrap();
73        assert_eq!(serialized, expected);
74    }
75
76    #[test]
77    fn test_serialize_outgoing_message_unsubscribe() {
78        let msg = OutgoingMessage::Unsubscribe {
79            req_id: Some(String::from("request_id")),
80            args: vec![Topic::Ticker(String::from("BTCUSDT"))],
81        };
82        let expected = r#"{"op":"unsubscribe","req_id":"request_id","args":["tickers.BTCUSDT"]}"#;
83        let serialized = serde_json::to_string(&msg).unwrap();
84        assert_eq!(serialized, expected);
85    }
86
87    #[test]
88    fn test_serialize_outgoing_message_auth() {
89        let msg = OutgoingMessage::Auth {
90            req_id: Some(String::from("request_id")),
91            args: (
92                String::from("api_key"),
93                1662350400000,
94                String::from("signature"),
95            ),
96        };
97        let expected =
98            r#"{"op":"auth","req_id":"request_id","args":["api_key",1662350400000,"signature"]}"#;
99        let serialized = serde_json::to_string(&msg).unwrap();
100        assert_eq!(serialized, expected);
101    }
102}