use ccxt_core::error::Error;
use serde_json::Value;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OkxErrorCode {
InvalidRequest,
InvalidApiKey,
InvalidSignature,
InvalidTimestamp,
RateLimitExceeded,
InvalidPassphrase,
BadSymbol,
ParameterError,
InsufficientFunds,
OrderNotFound,
InsufficientBalance,
Unknown(i64),
}
impl OkxErrorCode {
pub fn from_code(code: &str) -> Self {
match code.parse::<i64>() {
Ok(50000) => OkxErrorCode::InvalidRequest,
Ok(50001) => OkxErrorCode::InvalidApiKey,
Ok(50002) => OkxErrorCode::InvalidSignature,
Ok(50003) => OkxErrorCode::InvalidTimestamp,
Ok(50004) => OkxErrorCode::RateLimitExceeded,
Ok(50005) => OkxErrorCode::InvalidPassphrase,
Ok(50011) => OkxErrorCode::BadSymbol,
Ok(51000) => OkxErrorCode::ParameterError,
Ok(51001) => OkxErrorCode::InsufficientFunds,
Ok(51008) => OkxErrorCode::InsufficientBalance,
Ok(51400) => OkxErrorCode::OrderNotFound,
Ok(n) => OkxErrorCode::Unknown(n),
Err(_) => OkxErrorCode::Unknown(0),
}
}
pub fn code(&self) -> i64 {
match self {
OkxErrorCode::InvalidRequest => 50000,
OkxErrorCode::InvalidApiKey => 50001,
OkxErrorCode::InvalidSignature => 50002,
OkxErrorCode::InvalidTimestamp => 50003,
OkxErrorCode::RateLimitExceeded => 50004,
OkxErrorCode::InvalidPassphrase => 50005,
OkxErrorCode::BadSymbol => 50011,
OkxErrorCode::ParameterError => 51000,
OkxErrorCode::InsufficientFunds => 51001,
OkxErrorCode::InsufficientBalance => 51008,
OkxErrorCode::OrderNotFound => 51400,
OkxErrorCode::Unknown(n) => *n,
}
}
}
pub fn parse_error(response: &Value) -> Error {
let code = response
.get("code")
.and_then(serde_json::Value::as_str)
.unwrap_or("unknown");
let msg = response
.get("msg")
.and_then(serde_json::Value::as_str)
.unwrap_or("Unknown error");
let error_code = OkxErrorCode::from_code(code);
match error_code {
OkxErrorCode::InvalidApiKey
| OkxErrorCode::InvalidSignature
| OkxErrorCode::InvalidTimestamp
| OkxErrorCode::InvalidPassphrase => Error::authentication(msg.to_string()),
OkxErrorCode::RateLimitExceeded => {
Error::rate_limit(msg.to_string(), Some(Duration::from_secs(1)))
}
OkxErrorCode::InvalidRequest | OkxErrorCode::ParameterError => {
Error::invalid_request(msg.to_string())
}
OkxErrorCode::InsufficientFunds | OkxErrorCode::InsufficientBalance => {
Error::insufficient_balance(msg.to_string())
}
OkxErrorCode::BadSymbol => Error::bad_symbol(msg.to_string()),
OkxErrorCode::OrderNotFound | OkxErrorCode::Unknown(_) => Error::exchange(code, msg),
}
}
pub fn is_error_response(response: &Value) -> bool {
response.get("code").and_then(serde_json::Value::as_str) != Some("0")
}
pub fn extract_error_code(response: &Value) -> &str {
response
.get("code")
.and_then(serde_json::Value::as_str)
.unwrap_or("unknown")
}
pub fn extract_error_message(response: &Value) -> &str {
response
.get("msg")
.and_then(serde_json::Value::as_str)
.unwrap_or("Unknown error")
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_parse_authentication_error_invalid_api_key() {
let response = json!({
"code": "50001",
"msg": "Invalid API key"
});
let error = parse_error(&response);
assert!(error.as_authentication().is_some());
assert!(error.to_string().contains("Invalid API key"));
}
#[test]
fn test_parse_authentication_error_invalid_signature() {
let response = json!({
"code": "50002",
"msg": "Invalid signature"
});
let error = parse_error(&response);
assert!(error.as_authentication().is_some());
assert!(error.to_string().contains("Invalid signature"));
}
#[test]
fn test_parse_authentication_error_invalid_passphrase() {
let response = json!({
"code": "50005",
"msg": "Invalid passphrase"
});
let error = parse_error(&response);
assert!(error.as_authentication().is_some());
assert!(error.to_string().contains("Invalid passphrase"));
}
#[test]
fn test_parse_rate_limit_error() {
let response = json!({
"code": "50004",
"msg": "Rate limit exceeded"
});
let error = parse_error(&response);
assert!(error.as_rate_limit().is_some());
let (msg, retry_after) = error.as_rate_limit().unwrap();
assert!(msg.contains("Rate limit"));
assert!(retry_after.is_some());
}
#[test]
fn test_parse_invalid_request_error() {
let response = json!({
"code": "50000",
"msg": "Invalid request parameters"
});
let error = parse_error(&response);
let display = error.to_string();
assert!(display.contains("Invalid request"));
}
#[test]
fn test_parse_parameter_error() {
let response = json!({
"code": "51000",
"msg": "Parameter error"
});
let error = parse_error(&response);
let display = error.to_string();
assert!(display.contains("Parameter error"));
}
#[test]
fn test_parse_insufficient_funds_error() {
let response = json!({
"code": "51001",
"msg": "Insufficient balance"
});
let error = parse_error(&response);
let display = error.to_string();
assert!(display.contains("Insufficient balance"));
}
#[test]
fn test_parse_insufficient_balance_error() {
let response = json!({
"code": "51008",
"msg": "Insufficient balance for order"
});
let error = parse_error(&response);
let display = error.to_string();
assert!(display.contains("Insufficient balance"));
}
#[test]
fn test_parse_bad_symbol_error() {
let response = json!({
"code": "50011",
"msg": "Invalid instrument"
});
let error = parse_error(&response);
let display = error.to_string();
assert!(display.contains("Bad symbol"));
}
#[test]
fn test_parse_order_not_found_error() {
let response = json!({
"code": "51400",
"msg": "Order does not exist"
});
let error = parse_error(&response);
let display = error.to_string();
assert!(display.contains("Order does not exist"));
}
#[test]
fn test_parse_unknown_error() {
let response = json!({
"code": "99999",
"msg": "Some unknown error"
});
let error = parse_error(&response);
let display = error.to_string();
assert!(display.contains("Some unknown error"));
}
#[test]
fn test_parse_error_missing_code() {
let response = json!({
"msg": "Error without code"
});
let error = parse_error(&response);
let display = error.to_string();
assert!(display.contains("Error without code"));
}
#[test]
fn test_parse_error_missing_message() {
let response = json!({
"code": "50001"
});
let error = parse_error(&response);
let display = error.to_string();
assert!(display.contains("Unknown error"));
}
#[test]
fn test_is_error_response_success() {
let response = json!({
"code": "0",
"msg": "success",
"data": []
});
assert!(!is_error_response(&response));
}
#[test]
fn test_is_error_response_error() {
let response = json!({
"code": "50001",
"msg": "Invalid API key"
});
assert!(is_error_response(&response));
}
#[test]
fn test_is_error_response_missing_code() {
let response = json!({
"msg": "No code field"
});
assert!(is_error_response(&response));
}
#[test]
fn test_extract_error_code() {
let response = json!({
"code": "50001",
"msg": "Invalid API key"
});
assert_eq!(extract_error_code(&response), "50001");
}
#[test]
fn test_extract_error_code_missing() {
let response = json!({
"msg": "No code"
});
assert_eq!(extract_error_code(&response), "unknown");
}
#[test]
fn test_extract_error_message() {
let response = json!({
"code": "50001",
"msg": "Invalid API key"
});
assert_eq!(extract_error_message(&response), "Invalid API key");
}
#[test]
fn test_extract_error_message_missing() {
let response = json!({
"code": "50001"
});
assert_eq!(extract_error_message(&response), "Unknown error");
}
#[test]
fn test_okx_error_code_from_code() {
assert_eq!(
OkxErrorCode::from_code("50000"),
OkxErrorCode::InvalidRequest
);
assert_eq!(
OkxErrorCode::from_code("50001"),
OkxErrorCode::InvalidApiKey
);
assert_eq!(
OkxErrorCode::from_code("50002"),
OkxErrorCode::InvalidSignature
);
assert_eq!(
OkxErrorCode::from_code("50003"),
OkxErrorCode::InvalidTimestamp
);
assert_eq!(
OkxErrorCode::from_code("50004"),
OkxErrorCode::RateLimitExceeded
);
assert_eq!(
OkxErrorCode::from_code("50005"),
OkxErrorCode::InvalidPassphrase
);
assert_eq!(OkxErrorCode::from_code("50011"), OkxErrorCode::BadSymbol);
assert_eq!(
OkxErrorCode::from_code("51000"),
OkxErrorCode::ParameterError
);
assert_eq!(
OkxErrorCode::from_code("51001"),
OkxErrorCode::InsufficientFunds
);
assert_eq!(
OkxErrorCode::from_code("51008"),
OkxErrorCode::InsufficientBalance
);
assert_eq!(
OkxErrorCode::from_code("51400"),
OkxErrorCode::OrderNotFound
);
assert_eq!(
OkxErrorCode::from_code("99999"),
OkxErrorCode::Unknown(99999)
);
assert_eq!(OkxErrorCode::from_code("invalid"), OkxErrorCode::Unknown(0));
}
#[test]
fn test_okx_error_code_code() {
assert_eq!(OkxErrorCode::InvalidRequest.code(), 50000);
assert_eq!(OkxErrorCode::InvalidApiKey.code(), 50001);
assert_eq!(OkxErrorCode::InvalidSignature.code(), 50002);
assert_eq!(OkxErrorCode::InvalidTimestamp.code(), 50003);
assert_eq!(OkxErrorCode::RateLimitExceeded.code(), 50004);
assert_eq!(OkxErrorCode::InvalidPassphrase.code(), 50005);
assert_eq!(OkxErrorCode::BadSymbol.code(), 50011);
assert_eq!(OkxErrorCode::ParameterError.code(), 51000);
assert_eq!(OkxErrorCode::InsufficientFunds.code(), 51001);
assert_eq!(OkxErrorCode::InsufficientBalance.code(), 51008);
assert_eq!(OkxErrorCode::OrderNotFound.code(), 51400);
assert_eq!(OkxErrorCode::Unknown(12345).code(), 12345);
}
}