barter_data/exchange/okx/
subscription.rs1use super::{channel::OkxChannel, market::OkxMarket};
2use crate::exchange::subscription::ExchangeSub;
3use barter_integration::{Validator, error::SocketError};
4use serde::{Deserialize, Serialize, Serializer, ser::SerializeStruct};
5
6impl Serialize for ExchangeSub<OkxChannel, OkxMarket> {
8 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
9 where
10 S: Serializer,
11 {
12 let mut state = serializer.serialize_struct("OkxSubArg", 2)?;
13 state.serialize_field("channel", self.channel.as_ref())?;
14 state.serialize_field("instId", self.market.as_ref())?;
15 state.end()
16 }
17}
18
19#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
44#[serde(tag = "event", rename_all = "lowercase")]
45pub enum OkxSubResponse {
46 #[serde(rename = "subscribe")]
47 Subscribed,
48 Error {
49 code: String,
50 #[serde(rename = "msg")]
51 message: String,
52 },
53}
54
55impl Validator for OkxSubResponse {
56 fn validate(self) -> Result<Self, SocketError>
57 where
58 Self: Sized,
59 {
60 match self {
61 Self::Subscribed => Ok(self),
62 Self::Error { code, message } => Err(SocketError::Subscribe(format!(
63 "received failure subscription response code: {code} with message: {message}",
64 ))),
65 }
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 mod de {
74 use super::*;
75
76 #[test]
77 fn test_okx_subscription_response() {
78 struct TestCase {
79 input: &'static str,
80 expected: Result<OkxSubResponse, SocketError>,
81 }
82
83 let cases = vec![
84 TestCase {
85 input: r#"
87 {
88 "event": "subscribe",
89 "args": {"channel": "trades", "instId": "BTC-USD-191227"}
90 }
91 "#,
92 expected: Ok(OkxSubResponse::Subscribed),
93 },
94 TestCase {
95 input: r#"
97 {
98 "event": "error",
99 "code": "60012",
100 "msg": "Invalid request: {\"op\": \"subscribe\", \"args\":[{ \"channel\" : \"trades\", \"instId\" : \"BTC-USD-191227\"}]}"
101 }
102 "#,
103 expected: Ok(OkxSubResponse::Error {
104 code: "60012".to_string(),
105 message: "Invalid request: {\"op\": \"subscribe\", \"args\":[{ \"channel\" : \"trades\", \"instId\" : \"BTC-USD-191227\"}]}".to_string()
106 }),
107 },
108 ];
109
110 for (index, test) in cases.into_iter().enumerate() {
111 let actual = serde_json::from_str::<OkxSubResponse>(test.input);
112 match (actual, test.expected) {
113 (Ok(actual), Ok(expected)) => {
114 assert_eq!(actual, expected, "TC{} failed", index)
115 }
116 (Err(_), Err(_)) => {
117 }
119 (actual, expected) => {
120 panic!(
122 "TC{index} failed because actual != expected. \nActual: {actual:?}\nExpected: {expected:?}\n"
123 );
124 }
125 }
126 }
127 }
128 }
129
130 #[test]
131 fn test_validate_okx_sub_response() {
132 struct TestCase {
133 input_response: OkxSubResponse,
134 is_valid: bool,
135 }
136
137 let cases = vec![
138 TestCase {
139 input_response: OkxSubResponse::Subscribed,
141 is_valid: true,
142 },
143 TestCase {
144 input_response: OkxSubResponse::Error {
146 code: "60012".to_string(),
147 message: "Invalid request: {\"op\": \"subscribe\", \"args\":[{ \"channel\" : \"trades\", \"instId\" : \"BTC-USD-191227\"}]}".to_string()
148 },
149 is_valid: false,
150 },
151 ];
152
153 for (index, test) in cases.into_iter().enumerate() {
154 let actual = test.input_response.validate().is_ok();
155 assert_eq!(actual, test.is_valid, "TestCase {} failed", index);
156 }
157 }
158}