1use serde::{Deserialize, Serialize};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
25#[serde(transparent)]
26pub struct ErrorCode(pub i64);
27
28impl ErrorCode {
29 pub const fn new(code: i64) -> Self {
30 Self(code)
31 }
32
33 pub const fn raw(self) -> i64 {
35 self.0
36 }
37}
38
39impl From<i64> for ErrorCode {
40 fn from(code: i64) -> Self {
41 Self(code)
42 }
43}
44
45impl From<ErrorCode> for i64 {
46 fn from(code: ErrorCode) -> Self {
47 code.0
48 }
49}
50
51impl std::fmt::Display for ErrorCode {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 write!(f, "{}", self.0)
54 }
55}
56
57impl ErrorCode {
60 pub const UNKNOWN: Self = Self(-1000);
62 pub const DISCONNECTED: Self = Self(-1001);
63 pub const UNAUTHORIZED: Self = Self(-1002);
64 pub const TOO_MANY_REQUESTS: Self = Self(-1003);
65 pub const UNEXPECTED_RESP: Self = Self(-1006);
66 pub const TIMEOUT: Self = Self(-1007);
67 pub const SERVER_BUSY: Self = Self(-1008);
68 pub const TOO_MANY_ORDERS: Self = Self(-1015);
69 pub const SERVICE_SHUTTING_DOWN: Self = Self(-1016);
70 pub const UNSUPPORTED_OPERATION: Self = Self(-1020);
71 pub const INVALID_TIMESTAMP: Self = Self(-1021);
72 pub const INVALID_SIGNATURE: Self = Self(-1022);
73 pub const TOO_MANY_CONNECTIONS: Self = Self(-1034);
74
75 pub const ILLEGAL_CHARS: Self = Self(-1100);
77 pub const TOO_MANY_PARAMETERS: Self = Self(-1101);
78 pub const MANDATORY_PARAM_EMPTY_OR_MALFORMED: Self = Self(-1102);
79 pub const UNKNOWN_PARAM: Self = Self(-1103);
80 pub const BAD_SYMBOL: Self = Self(-1121);
81 pub const INVALID_LISTEN_KEY: Self = Self(-1125);
82 pub const TOO_MANY_MESSAGES: Self = Self(-1181);
83 pub const TOO_MANY_SUBSCRIPTIONS: Self = Self(-1191);
84
85 pub const NEW_ORDER_REJECTED: Self = Self(-2010);
87 pub const CANCEL_REJECTED: Self = Self(-2011);
88 pub const NO_SUCH_ORDER: Self = Self(-2013);
89 pub const BAD_API_KEY_FMT: Self = Self(-2014);
90 pub const REJECTED_MBX_KEY: Self = Self(-2015);
91 pub const NO_TRADING_WINDOW: Self = Self(-2016);
92 pub const ORDER_AMEND_REJECTED: Self = Self(-2038);
93 pub const CLIENT_ORDER_ID_INVALID: Self = Self(-2039);
94}
95
96impl ErrorCode {
99 pub fn is_auth(self) -> bool {
106 matches!(
107 self,
108 Self::UNAUTHORIZED
109 | Self::INVALID_TIMESTAMP
110 | Self::INVALID_SIGNATURE
111 | Self::BAD_API_KEY_FMT
112 | Self::REJECTED_MBX_KEY
113 )
114 }
115
116 pub fn is_invalid_timestamp(self) -> bool {
119 self == Self::INVALID_TIMESTAMP
120 }
121
122 pub fn is_invalid_signature(self) -> bool {
125 self == Self::INVALID_SIGNATURE
126 }
127
128 pub fn is_bad_api_key_format(self) -> bool {
130 self == Self::BAD_API_KEY_FMT
131 }
132
133 pub fn is_api_key_rejected(self) -> bool {
137 self == Self::REJECTED_MBX_KEY
138 }
139
140 pub fn is_wrong_permissions(self) -> bool {
144 self == Self::REJECTED_MBX_KEY
145 }
146
147 pub fn is_bad_request(self) -> bool {
153 (-1199..=-1100).contains(&self.0)
154 }
155
156 pub fn is_rate_limited(self) -> bool {
162 matches!(
163 self,
164 Self::TOO_MANY_REQUESTS
165 | Self::TOO_MANY_ORDERS
166 | Self::TOO_MANY_CONNECTIONS
167 | Self::TOO_MANY_MESSAGES
168 | Self::TOO_MANY_SUBSCRIPTIONS
169 )
170 }
171
172 pub fn is_server_error(self) -> bool {
178 matches!(
179 self,
180 Self::UNKNOWN
181 | Self::DISCONNECTED
182 | Self::UNEXPECTED_RESP
183 | Self::TIMEOUT
184 | Self::SERVER_BUSY
185 | Self::SERVICE_SHUTTING_DOWN
186 )
187 }
188
189 pub fn is_transient(self) -> bool {
192 self.is_server_error() || self.is_rate_limited()
193 }
194
195 pub fn is_order_rejected(self) -> bool {
201 matches!(
202 self,
203 Self::NEW_ORDER_REJECTED | Self::CANCEL_REJECTED | Self::ORDER_AMEND_REJECTED
204 )
205 }
206
207 pub fn is_no_such_order(self) -> bool {
210 self == Self::NO_SUCH_ORDER
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217 use crate::serde::deserialize_json;
218
219 #[test]
220 fn deserializes_from_bare_integer() {
221 let code: ErrorCode = deserialize_json("-1102").unwrap();
222 assert_eq!(code, ErrorCode::MANDATORY_PARAM_EMPTY_OR_MALFORMED);
223 assert_eq!(code.raw(), -1102);
224 }
225
226 #[test]
227 fn unknown_code_still_parses() {
228 let code: ErrorCode = deserialize_json("-9999").unwrap();
230 assert_eq!(code.raw(), -9999);
231 assert!(!code.is_auth());
232 assert!(!code.is_bad_request());
233 assert!(!code.is_transient());
234 }
235
236 #[test]
237 fn classification_predicates() {
238 assert!(ErrorCode::UNAUTHORIZED.is_auth());
239 assert!(ErrorCode::INVALID_SIGNATURE.is_auth());
240 assert!(ErrorCode::REJECTED_MBX_KEY.is_auth());
241 assert!(ErrorCode::REJECTED_MBX_KEY.is_wrong_permissions());
242 assert!(ErrorCode::REJECTED_MBX_KEY.is_api_key_rejected());
243
244 assert!(ErrorCode::BAD_SYMBOL.is_bad_request());
245 assert!(ErrorCode::MANDATORY_PARAM_EMPTY_OR_MALFORMED.is_bad_request());
246 assert!(!ErrorCode::UNKNOWN.is_bad_request());
247
248 assert!(ErrorCode::TOO_MANY_REQUESTS.is_rate_limited());
249 assert!(ErrorCode::TOO_MANY_REQUESTS.is_transient());
250
251 assert!(ErrorCode::SERVICE_SHUTTING_DOWN.is_server_error());
252 assert!(ErrorCode::SERVICE_SHUTTING_DOWN.is_transient());
253
254 assert!(ErrorCode::NEW_ORDER_REJECTED.is_order_rejected());
255 assert!(ErrorCode::NO_SUCH_ORDER.is_no_such_order());
256 }
257}