use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[serde(transparent)]
pub struct ErrorCode(pub i64);
impl ErrorCode {
pub const fn new(code: i64) -> Self {
Self(code)
}
pub const fn raw(self) -> i64 {
self.0
}
}
impl From<i64> for ErrorCode {
fn from(code: i64) -> Self {
Self(code)
}
}
impl From<ErrorCode> for i64 {
fn from(code: ErrorCode) -> Self {
code.0
}
}
impl std::fmt::Display for ErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl ErrorCode {
pub const UNKNOWN: Self = Self(-1000);
pub const DISCONNECTED: Self = Self(-1001);
pub const UNAUTHORIZED: Self = Self(-1002);
pub const TOO_MANY_REQUESTS: Self = Self(-1003);
pub const UNEXPECTED_RESP: Self = Self(-1006);
pub const TIMEOUT: Self = Self(-1007);
pub const SERVER_BUSY: Self = Self(-1008);
pub const TOO_MANY_ORDERS: Self = Self(-1015);
pub const SERVICE_SHUTTING_DOWN: Self = Self(-1016);
pub const UNSUPPORTED_OPERATION: Self = Self(-1020);
pub const INVALID_TIMESTAMP: Self = Self(-1021);
pub const INVALID_SIGNATURE: Self = Self(-1022);
pub const TOO_MANY_CONNECTIONS: Self = Self(-1034);
pub const ILLEGAL_CHARS: Self = Self(-1100);
pub const TOO_MANY_PARAMETERS: Self = Self(-1101);
pub const MANDATORY_PARAM_EMPTY_OR_MALFORMED: Self = Self(-1102);
pub const UNKNOWN_PARAM: Self = Self(-1103);
pub const BAD_SYMBOL: Self = Self(-1121);
pub const INVALID_LISTEN_KEY: Self = Self(-1125);
pub const TOO_MANY_MESSAGES: Self = Self(-1181);
pub const TOO_MANY_SUBSCRIPTIONS: Self = Self(-1191);
pub const NEW_ORDER_REJECTED: Self = Self(-2010);
pub const CANCEL_REJECTED: Self = Self(-2011);
pub const NO_SUCH_ORDER: Self = Self(-2013);
pub const BAD_API_KEY_FMT: Self = Self(-2014);
pub const REJECTED_MBX_KEY: Self = Self(-2015);
pub const NO_TRADING_WINDOW: Self = Self(-2016);
pub const ORDER_AMEND_REJECTED: Self = Self(-2038);
pub const CLIENT_ORDER_ID_INVALID: Self = Self(-2039);
}
impl ErrorCode {
pub fn is_auth(self) -> bool {
matches!(
self,
Self::UNAUTHORIZED
| Self::INVALID_TIMESTAMP
| Self::INVALID_SIGNATURE
| Self::BAD_API_KEY_FMT
| Self::REJECTED_MBX_KEY
)
}
pub fn is_invalid_timestamp(self) -> bool {
self == Self::INVALID_TIMESTAMP
}
pub fn is_invalid_signature(self) -> bool {
self == Self::INVALID_SIGNATURE
}
pub fn is_bad_api_key_format(self) -> bool {
self == Self::BAD_API_KEY_FMT
}
pub fn is_api_key_rejected(self) -> bool {
self == Self::REJECTED_MBX_KEY
}
pub fn is_wrong_permissions(self) -> bool {
self == Self::REJECTED_MBX_KEY
}
pub fn is_bad_request(self) -> bool {
(-1199..=-1100).contains(&self.0)
}
pub fn is_rate_limited(self) -> bool {
matches!(
self,
Self::TOO_MANY_REQUESTS
| Self::TOO_MANY_ORDERS
| Self::TOO_MANY_CONNECTIONS
| Self::TOO_MANY_MESSAGES
| Self::TOO_MANY_SUBSCRIPTIONS
)
}
pub fn is_server_error(self) -> bool {
matches!(
self,
Self::UNKNOWN
| Self::DISCONNECTED
| Self::UNEXPECTED_RESP
| Self::TIMEOUT
| Self::SERVER_BUSY
| Self::SERVICE_SHUTTING_DOWN
)
}
pub fn is_transient(self) -> bool {
self.is_server_error() || self.is_rate_limited()
}
pub fn is_order_rejected(self) -> bool {
matches!(
self,
Self::NEW_ORDER_REJECTED | Self::CANCEL_REJECTED | Self::ORDER_AMEND_REJECTED
)
}
pub fn is_no_such_order(self) -> bool {
self == Self::NO_SUCH_ORDER
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::serde::deserialize_json;
#[test]
fn deserializes_from_bare_integer() {
let code: ErrorCode = deserialize_json("-1102").unwrap();
assert_eq!(code, ErrorCode::MANDATORY_PARAM_EMPTY_OR_MALFORMED);
assert_eq!(code.raw(), -1102);
}
#[test]
fn unknown_code_still_parses() {
let code: ErrorCode = deserialize_json("-9999").unwrap();
assert_eq!(code.raw(), -9999);
assert!(!code.is_auth());
assert!(!code.is_bad_request());
assert!(!code.is_transient());
}
#[test]
fn classification_predicates() {
assert!(ErrorCode::UNAUTHORIZED.is_auth());
assert!(ErrorCode::INVALID_SIGNATURE.is_auth());
assert!(ErrorCode::REJECTED_MBX_KEY.is_auth());
assert!(ErrorCode::REJECTED_MBX_KEY.is_wrong_permissions());
assert!(ErrorCode::REJECTED_MBX_KEY.is_api_key_rejected());
assert!(ErrorCode::BAD_SYMBOL.is_bad_request());
assert!(ErrorCode::MANDATORY_PARAM_EMPTY_OR_MALFORMED.is_bad_request());
assert!(!ErrorCode::UNKNOWN.is_bad_request());
assert!(ErrorCode::TOO_MANY_REQUESTS.is_rate_limited());
assert!(ErrorCode::TOO_MANY_REQUESTS.is_transient());
assert!(ErrorCode::SERVICE_SHUTTING_DOWN.is_server_error());
assert!(ErrorCode::SERVICE_SHUTTING_DOWN.is_transient());
assert!(ErrorCode::NEW_ORDER_REJECTED.is_order_rejected());
assert!(ErrorCode::NO_SUCH_ORDER.is_no_such_order());
}
}