bybit/ws/
outgoing_message.rs1use 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(
43 api_key: SensitiveString,
44 api_secret: SensitiveString,
45 req_id: Option<String>,
46 recv_window: Timestamp,
47) -> OutgoingMessage {
48 create_outgoing_message_auth_at(api_key, api_secret, req_id, recv_window, timestamp())
49}
50
51pub fn create_outgoing_message_auth_at(
54 api_key: SensitiveString,
55 api_secret: SensitiveString,
56 req_id: Option<String>,
57 recv_window: Timestamp,
58 now: Timestamp,
59) -> OutgoingMessage {
60 let api_key = api_key.expose().to_string();
61 let expires = now + recv_window;
62
63 let signature = create_stream_signature(expires, api_secret);
64
65 OutgoingMessage::Auth {
66 req_id,
67 args: (api_key, expires, signature),
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use crate::*;
74
75 use super::*;
76
77 #[test]
78 fn test_serialize_outgoing_message_subscribe() {
79 let msg = OutgoingMessage::Subscribe {
80 req_id: Some(String::from("request_id")),
81 args: vec![
82 Topic::Ticker(String::from("BTCUSDT")),
83 Topic::Order(Category::Linear),
84 ],
85 };
86 let expected =
87 r#"{"op":"subscribe","req_id":"request_id","args":["tickers.BTCUSDT","order.linear"]}"#;
88 let serialized = serde_json::to_string(&msg).unwrap();
89 assert_eq!(serialized, expected);
90 }
91
92 #[test]
93 fn test_serialize_outgoing_message_unsubscribe() {
94 let msg = OutgoingMessage::Unsubscribe {
95 req_id: Some(String::from("request_id")),
96 args: vec![Topic::Ticker(String::from("BTCUSDT"))],
97 };
98 let expected = r#"{"op":"unsubscribe","req_id":"request_id","args":["tickers.BTCUSDT"]}"#;
99 let serialized = serde_json::to_string(&msg).unwrap();
100 assert_eq!(serialized, expected);
101 }
102
103 #[test]
104 fn test_serialize_outgoing_message_auth() {
105 let msg = OutgoingMessage::Auth {
106 req_id: Some(String::from("request_id")),
107 args: (
108 String::from("api_key"),
109 1662350400000,
110 String::from("signature"),
111 ),
112 };
113 let expected =
114 r#"{"op":"auth","req_id":"request_id","args":["api_key",1662350400000,"signature"]}"#;
115 let serialized = serde_json::to_string(&msg).unwrap();
116 assert_eq!(serialized, expected);
117 }
118}