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 type Error = SocketError;
57
58 fn validate(self) -> Result<Self, SocketError>
59 where
60 Self: Sized,
61 {
62 match self {
63 Self::Subscribed => Ok(self),
64 Self::Error { code, message } => Err(SocketError::Subscribe(format!(
65 "received failure subscription response code: {code} with message: {message}",
66 ))),
67 }
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 mod de {
76 use super::*;
77
78 #[test]
79 fn test_okx_subscription_response() {
80 struct TestCase {
81 input: &'static str,
82 expected: Result<OkxSubResponse, SocketError>,
83 }
84
85 let cases = vec![
86 TestCase {
87 input: r#"
89 {
90 "event": "subscribe",
91 "args": {"channel": "trades", "instId": "BTC-USD-191227"}
92 }
93 "#,
94 expected: Ok(OkxSubResponse::Subscribed),
95 },
96 TestCase {
97 input: r#"
99 {
100 "event": "error",
101 "code": "60012",
102 "msg": "Invalid request: {\"op\": \"subscribe\", \"args\":[{ \"channel\" : \"trades\", \"instId\" : \"BTC-USD-191227\"}]}"
103 }
104 "#,
105 expected: Ok(OkxSubResponse::Error {
106 code: "60012".to_string(),
107 message: "Invalid request: {\"op\": \"subscribe\", \"args\":[{ \"channel\" : \"trades\", \"instId\" : \"BTC-USD-191227\"}]}".to_string()
108 }),
109 },
110 ];
111
112 for (index, test) in cases.into_iter().enumerate() {
113 let actual = serde_json::from_str::<OkxSubResponse>(test.input);
114 match (actual, test.expected) {
115 (Ok(actual), Ok(expected)) => {
116 assert_eq!(actual, expected, "TC{} failed", index)
117 }
118 (Err(_), Err(_)) => {
119 }
121 (actual, expected) => {
122 panic!(
124 "TC{index} failed because actual != expected. \nActual: {actual:?}\nExpected: {expected:?}\n"
125 );
126 }
127 }
128 }
129 }
130 }
131
132 #[test]
133 fn test_validate_okx_sub_response() {
134 struct TestCase {
135 input_response: OkxSubResponse,
136 is_valid: bool,
137 }
138
139 let cases = vec![
140 TestCase {
141 input_response: OkxSubResponse::Subscribed,
143 is_valid: true,
144 },
145 TestCase {
146 input_response: OkxSubResponse::Error {
148 code: "60012".to_string(),
149 message: "Invalid request: {\"op\": \"subscribe\", \"args\":[{ \"channel\" : \"trades\", \"instId\" : \"BTC-USD-191227\"}]}".to_string()
150 },
151 is_valid: false,
152 },
153 ];
154
155 for (index, test) in cases.into_iter().enumerate() {
156 let actual = test.input_response.validate().is_ok();
157 assert_eq!(actual, test.is_valid, "TestCase {} failed", index);
158 }
159 }
160}