1use ccxt_core::error::Error;
7use serde_json::Value;
8use std::time::Duration;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum OkxErrorCode {
15 InvalidRequest,
17 InvalidApiKey,
19 InvalidSignature,
21 InvalidTimestamp,
23 RateLimitExceeded,
25 InvalidPassphrase,
27 BadSymbol,
29 ParameterError,
31 InsufficientFunds,
33 OrderNotFound,
35 InsufficientBalance,
37 Unknown(i64),
39}
40
41impl OkxErrorCode {
42 pub fn from_code(code: &str) -> Self {
44 match code.parse::<i64>() {
45 Ok(50000) => OkxErrorCode::InvalidRequest,
46 Ok(50001) => OkxErrorCode::InvalidApiKey,
47 Ok(50002) => OkxErrorCode::InvalidSignature,
48 Ok(50003) => OkxErrorCode::InvalidTimestamp,
49 Ok(50004) => OkxErrorCode::RateLimitExceeded,
50 Ok(50005) => OkxErrorCode::InvalidPassphrase,
51 Ok(50011) => OkxErrorCode::BadSymbol,
52 Ok(51000) => OkxErrorCode::ParameterError,
53 Ok(51001) => OkxErrorCode::InsufficientFunds,
54 Ok(51008) => OkxErrorCode::InsufficientBalance,
55 Ok(51400) => OkxErrorCode::OrderNotFound,
56 Ok(n) => OkxErrorCode::Unknown(n),
57 Err(_) => OkxErrorCode::Unknown(0),
58 }
59 }
60
61 pub fn code(&self) -> i64 {
63 match self {
64 OkxErrorCode::InvalidRequest => 50000,
65 OkxErrorCode::InvalidApiKey => 50001,
66 OkxErrorCode::InvalidSignature => 50002,
67 OkxErrorCode::InvalidTimestamp => 50003,
68 OkxErrorCode::RateLimitExceeded => 50004,
69 OkxErrorCode::InvalidPassphrase => 50005,
70 OkxErrorCode::BadSymbol => 50011,
71 OkxErrorCode::ParameterError => 51000,
72 OkxErrorCode::InsufficientFunds => 51001,
73 OkxErrorCode::InsufficientBalance => 51008,
74 OkxErrorCode::OrderNotFound => 51400,
75 OkxErrorCode::Unknown(n) => *n,
76 }
77 }
78}
79
80pub fn parse_error(response: &Value) -> Error {
114 let code = response
115 .get("code")
116 .and_then(|v| v.as_str())
117 .unwrap_or("unknown");
118
119 let msg = response
120 .get("msg")
121 .and_then(|v| v.as_str())
122 .unwrap_or("Unknown error");
123
124 let error_code = OkxErrorCode::from_code(code);
125
126 match error_code {
127 OkxErrorCode::InvalidApiKey
128 | OkxErrorCode::InvalidSignature
129 | OkxErrorCode::InvalidTimestamp
130 | OkxErrorCode::InvalidPassphrase => Error::authentication(msg.to_string()),
131 OkxErrorCode::RateLimitExceeded => {
132 Error::rate_limit(msg.to_string(), Some(Duration::from_secs(1)))
134 }
135 OkxErrorCode::InvalidRequest | OkxErrorCode::ParameterError => {
136 Error::invalid_request(msg.to_string())
137 }
138 OkxErrorCode::InsufficientFunds | OkxErrorCode::InsufficientBalance => {
139 Error::insufficient_balance(msg.to_string())
140 }
141 OkxErrorCode::BadSymbol => Error::bad_symbol(msg.to_string()),
142 OkxErrorCode::OrderNotFound => Error::exchange(code, msg),
143 OkxErrorCode::Unknown(_) => Error::exchange(code, msg),
144 }
145}
146
147pub fn is_error_response(response: &Value) -> bool {
159 response
160 .get("code")
161 .and_then(|v| v.as_str())
162 .map(|code| code != "0")
163 .unwrap_or(true)
164}
165
166pub fn extract_error_code(response: &Value) -> &str {
176 response
177 .get("code")
178 .and_then(|v| v.as_str())
179 .unwrap_or("unknown")
180}
181
182pub fn extract_error_message(response: &Value) -> &str {
192 response
193 .get("msg")
194 .and_then(|v| v.as_str())
195 .unwrap_or("Unknown error")
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201 use serde_json::json;
202
203 #[test]
204 fn test_parse_authentication_error_invalid_api_key() {
205 let response = json!({
206 "code": "50001",
207 "msg": "Invalid API key"
208 });
209
210 let error = parse_error(&response);
211 assert!(error.as_authentication().is_some());
212 assert!(error.to_string().contains("Invalid API key"));
213 }
214
215 #[test]
216 fn test_parse_authentication_error_invalid_signature() {
217 let response = json!({
218 "code": "50002",
219 "msg": "Invalid signature"
220 });
221
222 let error = parse_error(&response);
223 assert!(error.as_authentication().is_some());
224 assert!(error.to_string().contains("Invalid signature"));
225 }
226
227 #[test]
228 fn test_parse_authentication_error_invalid_passphrase() {
229 let response = json!({
230 "code": "50005",
231 "msg": "Invalid passphrase"
232 });
233
234 let error = parse_error(&response);
235 assert!(error.as_authentication().is_some());
236 assert!(error.to_string().contains("Invalid passphrase"));
237 }
238
239 #[test]
240 fn test_parse_rate_limit_error() {
241 let response = json!({
242 "code": "50004",
243 "msg": "Rate limit exceeded"
244 });
245
246 let error = parse_error(&response);
247 assert!(error.as_rate_limit().is_some());
248 let (msg, retry_after) = error.as_rate_limit().unwrap();
249 assert!(msg.contains("Rate limit"));
250 assert!(retry_after.is_some());
251 }
252
253 #[test]
254 fn test_parse_invalid_request_error() {
255 let response = json!({
256 "code": "50000",
257 "msg": "Invalid request parameters"
258 });
259
260 let error = parse_error(&response);
261 let display = error.to_string();
262 assert!(display.contains("Invalid request"));
263 }
264
265 #[test]
266 fn test_parse_parameter_error() {
267 let response = json!({
268 "code": "51000",
269 "msg": "Parameter error"
270 });
271
272 let error = parse_error(&response);
273 let display = error.to_string();
274 assert!(display.contains("Parameter error"));
275 }
276
277 #[test]
278 fn test_parse_insufficient_funds_error() {
279 let response = json!({
280 "code": "51001",
281 "msg": "Insufficient balance"
282 });
283
284 let error = parse_error(&response);
285 let display = error.to_string();
286 assert!(display.contains("Insufficient balance"));
287 }
288
289 #[test]
290 fn test_parse_insufficient_balance_error() {
291 let response = json!({
292 "code": "51008",
293 "msg": "Insufficient balance for order"
294 });
295
296 let error = parse_error(&response);
297 let display = error.to_string();
298 assert!(display.contains("Insufficient balance"));
299 }
300
301 #[test]
302 fn test_parse_bad_symbol_error() {
303 let response = json!({
304 "code": "50011",
305 "msg": "Invalid instrument"
306 });
307
308 let error = parse_error(&response);
309 let display = error.to_string();
310 assert!(display.contains("Bad symbol"));
311 }
312
313 #[test]
314 fn test_parse_order_not_found_error() {
315 let response = json!({
316 "code": "51400",
317 "msg": "Order does not exist"
318 });
319
320 let error = parse_error(&response);
321 let display = error.to_string();
322 assert!(display.contains("Order does not exist"));
323 }
324
325 #[test]
326 fn test_parse_unknown_error() {
327 let response = json!({
328 "code": "99999",
329 "msg": "Some unknown error"
330 });
331
332 let error = parse_error(&response);
333 let display = error.to_string();
334 assert!(display.contains("Some unknown error"));
335 }
336
337 #[test]
338 fn test_parse_error_missing_code() {
339 let response = json!({
340 "msg": "Error without code"
341 });
342
343 let error = parse_error(&response);
344 let display = error.to_string();
345 assert!(display.contains("Error without code"));
346 }
347
348 #[test]
349 fn test_parse_error_missing_message() {
350 let response = json!({
351 "code": "50001"
352 });
353
354 let error = parse_error(&response);
355 let display = error.to_string();
356 assert!(display.contains("Unknown error"));
357 }
358
359 #[test]
360 fn test_is_error_response_success() {
361 let response = json!({
362 "code": "0",
363 "msg": "success",
364 "data": []
365 });
366
367 assert!(!is_error_response(&response));
368 }
369
370 #[test]
371 fn test_is_error_response_error() {
372 let response = json!({
373 "code": "50001",
374 "msg": "Invalid API key"
375 });
376
377 assert!(is_error_response(&response));
378 }
379
380 #[test]
381 fn test_is_error_response_missing_code() {
382 let response = json!({
383 "msg": "No code field"
384 });
385
386 assert!(is_error_response(&response));
388 }
389
390 #[test]
391 fn test_extract_error_code() {
392 let response = json!({
393 "code": "50001",
394 "msg": "Invalid API key"
395 });
396
397 assert_eq!(extract_error_code(&response), "50001");
398 }
399
400 #[test]
401 fn test_extract_error_code_missing() {
402 let response = json!({
403 "msg": "No code"
404 });
405
406 assert_eq!(extract_error_code(&response), "unknown");
407 }
408
409 #[test]
410 fn test_extract_error_message() {
411 let response = json!({
412 "code": "50001",
413 "msg": "Invalid API key"
414 });
415
416 assert_eq!(extract_error_message(&response), "Invalid API key");
417 }
418
419 #[test]
420 fn test_extract_error_message_missing() {
421 let response = json!({
422 "code": "50001"
423 });
424
425 assert_eq!(extract_error_message(&response), "Unknown error");
426 }
427
428 #[test]
429 fn test_okx_error_code_from_code() {
430 assert_eq!(
431 OkxErrorCode::from_code("50000"),
432 OkxErrorCode::InvalidRequest
433 );
434 assert_eq!(
435 OkxErrorCode::from_code("50001"),
436 OkxErrorCode::InvalidApiKey
437 );
438 assert_eq!(
439 OkxErrorCode::from_code("50002"),
440 OkxErrorCode::InvalidSignature
441 );
442 assert_eq!(
443 OkxErrorCode::from_code("50003"),
444 OkxErrorCode::InvalidTimestamp
445 );
446 assert_eq!(
447 OkxErrorCode::from_code("50004"),
448 OkxErrorCode::RateLimitExceeded
449 );
450 assert_eq!(
451 OkxErrorCode::from_code("50005"),
452 OkxErrorCode::InvalidPassphrase
453 );
454 assert_eq!(OkxErrorCode::from_code("50011"), OkxErrorCode::BadSymbol);
455 assert_eq!(
456 OkxErrorCode::from_code("51000"),
457 OkxErrorCode::ParameterError
458 );
459 assert_eq!(
460 OkxErrorCode::from_code("51001"),
461 OkxErrorCode::InsufficientFunds
462 );
463 assert_eq!(
464 OkxErrorCode::from_code("51008"),
465 OkxErrorCode::InsufficientBalance
466 );
467 assert_eq!(
468 OkxErrorCode::from_code("51400"),
469 OkxErrorCode::OrderNotFound
470 );
471 assert_eq!(
472 OkxErrorCode::from_code("99999"),
473 OkxErrorCode::Unknown(99999)
474 );
475 assert_eq!(OkxErrorCode::from_code("invalid"), OkxErrorCode::Unknown(0));
476 }
477
478 #[test]
479 fn test_okx_error_code_code() {
480 assert_eq!(OkxErrorCode::InvalidRequest.code(), 50000);
481 assert_eq!(OkxErrorCode::InvalidApiKey.code(), 50001);
482 assert_eq!(OkxErrorCode::InvalidSignature.code(), 50002);
483 assert_eq!(OkxErrorCode::InvalidTimestamp.code(), 50003);
484 assert_eq!(OkxErrorCode::RateLimitExceeded.code(), 50004);
485 assert_eq!(OkxErrorCode::InvalidPassphrase.code(), 50005);
486 assert_eq!(OkxErrorCode::BadSymbol.code(), 50011);
487 assert_eq!(OkxErrorCode::ParameterError.code(), 51000);
488 assert_eq!(OkxErrorCode::InsufficientFunds.code(), 51001);
489 assert_eq!(OkxErrorCode::InsufficientBalance.code(), 51008);
490 assert_eq!(OkxErrorCode::OrderNotFound.code(), 51400);
491 assert_eq!(OkxErrorCode::Unknown(12345).code(), 12345);
492 }
493}