use ccxt_core::error::Error;
use serde_json::Value;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BitgetErrorCode {
InvalidApiKey,
InvalidSignature,
RateLimitExceeded,
InvalidRequest,
InsufficientFunds,
BadSymbol,
OrderNotFound,
Unknown(i64),
}
impl BitgetErrorCode {
pub fn from_code(code: &str) -> Self {
match code.parse::<i64>() {
Ok(40001) => BitgetErrorCode::InvalidApiKey,
Ok(40002) => BitgetErrorCode::InvalidSignature,
Ok(40003) => BitgetErrorCode::RateLimitExceeded,
Ok(40004) => BitgetErrorCode::InvalidRequest,
Ok(40005) => BitgetErrorCode::InsufficientFunds,
Ok(40006) => BitgetErrorCode::BadSymbol,
Ok(40007) => BitgetErrorCode::OrderNotFound,
Ok(n) => BitgetErrorCode::Unknown(n),
Err(_) => BitgetErrorCode::Unknown(0),
}
}
pub fn code(&self) -> i64 {
match self {
BitgetErrorCode::InvalidApiKey => 40001,
BitgetErrorCode::InvalidSignature => 40002,
BitgetErrorCode::RateLimitExceeded => 40003,
BitgetErrorCode::InvalidRequest => 40004,
BitgetErrorCode::InsufficientFunds => 40005,
BitgetErrorCode::BadSymbol => 40006,
BitgetErrorCode::OrderNotFound => 40007,
BitgetErrorCode::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 = BitgetErrorCode::from_code(code);
match error_code {
BitgetErrorCode::InvalidApiKey | BitgetErrorCode::InvalidSignature => {
Error::authentication(msg.to_string())
}
BitgetErrorCode::RateLimitExceeded => {
Error::rate_limit(msg.to_string(), Some(Duration::from_secs(1)))
}
BitgetErrorCode::InvalidRequest => Error::invalid_request(msg.to_string()),
BitgetErrorCode::InsufficientFunds => Error::insufficient_balance(msg.to_string()),
BitgetErrorCode::BadSymbol => Error::bad_symbol(msg.to_string()),
BitgetErrorCode::OrderNotFound | BitgetErrorCode::Unknown(_) => Error::exchange(code, msg),
}
}
pub fn is_error_response(response: &Value) -> bool {
response.get("code").and_then(serde_json::Value::as_str) != Some("00000")
}
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 {
#![allow(clippy::disallowed_methods)]
use super::*;
use serde_json::json;
#[test]
fn test_parse_authentication_error_invalid_api_key() {
let response = json!({
"code": "40001",
"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": "40002",
"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_rate_limit_error() {
let response = json!({
"code": "40003",
"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": "40004",
"msg": "Invalid request parameters"
});
let error = parse_error(&response);
let display = error.to_string();
assert!(display.contains("Invalid request"));
}
#[test]
fn test_parse_insufficient_funds_error() {
let response = json!({
"code": "40005",
"msg": "Insufficient balance"
});
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": "40006",
"msg": "Invalid trading pair"
});
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": "40007",
"msg": "Order not found"
});
let error = parse_error(&response);
let display = error.to_string();
assert!(display.contains("Order not found"));
}
#[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": "40001"
});
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": "00000",
"msg": "success",
"data": {}
});
assert!(!is_error_response(&response));
}
#[test]
fn test_is_error_response_error() {
let response = json!({
"code": "40001",
"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": "40001",
"msg": "Invalid API key"
});
assert_eq!(extract_error_code(&response), "40001");
}
#[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": "40001",
"msg": "Invalid API key"
});
assert_eq!(extract_error_message(&response), "Invalid API key");
}
#[test]
fn test_extract_error_message_missing() {
let response = json!({
"code": "40001"
});
assert_eq!(extract_error_message(&response), "Unknown error");
}
#[test]
fn test_bitget_error_code_from_code() {
assert_eq!(
BitgetErrorCode::from_code("40001"),
BitgetErrorCode::InvalidApiKey
);
assert_eq!(
BitgetErrorCode::from_code("40002"),
BitgetErrorCode::InvalidSignature
);
assert_eq!(
BitgetErrorCode::from_code("40003"),
BitgetErrorCode::RateLimitExceeded
);
assert_eq!(
BitgetErrorCode::from_code("40004"),
BitgetErrorCode::InvalidRequest
);
assert_eq!(
BitgetErrorCode::from_code("40005"),
BitgetErrorCode::InsufficientFunds
);
assert_eq!(
BitgetErrorCode::from_code("40006"),
BitgetErrorCode::BadSymbol
);
assert_eq!(
BitgetErrorCode::from_code("40007"),
BitgetErrorCode::OrderNotFound
);
assert_eq!(
BitgetErrorCode::from_code("99999"),
BitgetErrorCode::Unknown(99999)
);
assert_eq!(
BitgetErrorCode::from_code("invalid"),
BitgetErrorCode::Unknown(0)
);
}
#[test]
fn test_bitget_error_code_code() {
assert_eq!(BitgetErrorCode::InvalidApiKey.code(), 40001);
assert_eq!(BitgetErrorCode::InvalidSignature.code(), 40002);
assert_eq!(BitgetErrorCode::RateLimitExceeded.code(), 40003);
assert_eq!(BitgetErrorCode::InvalidRequest.code(), 40004);
assert_eq!(BitgetErrorCode::InsufficientFunds.code(), 40005);
assert_eq!(BitgetErrorCode::BadSymbol.code(), 40006);
assert_eq!(BitgetErrorCode::OrderNotFound.code(), 40007);
assert_eq!(BitgetErrorCode::Unknown(12345).code(), 12345);
}
}