1use crate::spot::SignQueryError;
2use num_traits::FromPrimitive;
3use reqwest::StatusCode;
4use std::fmt::{Display, Formatter};
5
6pub mod account_information;
7pub mod avg_price;
8pub mod cancel_all_open_orders_on_a_symbol;
9pub mod cancel_order;
10pub mod create_user_data_stream;
11pub mod default_symbols;
12pub mod depth;
13pub mod enums;
14pub mod exchange_information;
15pub mod get_open_orders;
16pub mod get_order;
17pub mod keep_alive_user_data_stream;
18pub mod klines;
19pub mod models;
20pub mod order;
21pub mod ping;
22pub mod query_order;
23pub mod time;
24pub mod trades;
25
26pub type ApiResult<T> = Result<T, ApiError>;
27
28#[derive(Debug, thiserror::Error)]
30pub enum ApiError {
31 #[error("Malformed request")]
33 MalformedRequest,
34
35 #[error("Web application firewall (WAF) violated")]
37 WebApplicationFirewallViolated,
38
39 #[error("Rate limit exceeded")]
41 RateLimitExceeded,
42
43 #[error("Internal server error")]
45 InternalServerError,
46
47 #[error("Reqwest error: {0}")]
48 ReqwestError(reqwest::Error),
49
50 #[error("Serde url encoded error: {0}")]
51 SerdeUrlencodedError(#[from] serde_urlencoded::ser::Error),
52
53 #[error("Serde json error: {0}")]
54 SerdeJsonError(#[from] serde_json::Error),
55
56 #[error("Unable to parse response")]
58 UnableToParseResponse,
59
60 #[error("Error response: {0:?}")]
61 ErrorResponse(#[from] ErrorResponse),
62
63 #[error("Sign query error: {0}")]
64 SignQueryError(#[from] SignQueryError),
65}
66
67impl From<reqwest::Error> for ApiError {
68 fn from(err: reqwest::Error) -> Self {
69 let status = match err.status() {
70 None => {
71 return Self::ReqwestError(err);
72 }
73 Some(status) => status,
74 };
75
76 match status {
77 StatusCode::BAD_REQUEST => Self::MalformedRequest,
78 StatusCode::FORBIDDEN => Self::WebApplicationFirewallViolated,
79 StatusCode::TOO_MANY_REQUESTS => Self::RateLimitExceeded,
80 StatusCode::INTERNAL_SERVER_ERROR => Self::InternalServerError,
81 _ => Self::ReqwestError(err),
82 }
83 }
84}
85
86#[derive(Debug, serde::Deserialize)]
87#[serde(untagged)]
88pub enum ApiResponse<T> {
89 Success(T),
90 Error(ErrorResponse),
91 ErrorStringifiedCode(ErrorResponseStringifiedCode),
92}
93
94impl<T> ApiResponse<T> {
95 pub fn into_result(self) -> Result<T, ErrorResponse> {
96 match self {
97 Self::Success(output) => Ok(output),
98 Self::Error(err) => Err(err),
99 Self::ErrorStringifiedCode(esc) => {
100 Err(esc.try_into().map_err(|_err| ErrorResponse {
101 msg: "Stringified error code cannot be parsed".to_string(),
102 code: ErrorCode::InvalidResponse,
103 _extend: None,
104 })?)
105 }
106 }
107 }
108
109 pub fn into_api_result(self) -> ApiResult<T> {
110 match self {
111 Self::Success(output) => Ok(output),
112 Self::Error(response) => Err(ApiError::ErrorResponse(response)),
113 Self::ErrorStringifiedCode(esc) => {
114 Err(ApiError::ErrorResponse(esc.try_into().map_err(|_err| {
115 ErrorResponse {
116 msg: "Stringified error code cannot be parsed".to_string(),
117 code: ErrorCode::InvalidResponse,
118 _extend: None,
119 }
120 })?))
121 }
122 }
123 }
124}
125
126#[derive(Debug, serde::Deserialize)]
127pub struct ErrorResponse {
128 pub code: ErrorCode,
129 pub msg: String,
130 pub _extend: Option<serde_json::Value>,
131}
132
133#[derive(Debug, serde::Deserialize)]
134pub struct ErrorResponseStringifiedCode {
135 pub code: String,
136 pub msg: String,
137 pub _extend: Option<serde_json::Value>,
138}
139
140impl TryFrom<ErrorResponseStringifiedCode> for ErrorResponse {
141 type Error = ();
142
143 fn try_from(value: ErrorResponseStringifiedCode) -> Result<Self, Self::Error> {
144 let code = match value.code.parse::<i32>() {
145 Ok(code) => code,
146 Err(_) => return Err(()),
147 };
148 let code = match ErrorCode::from_i32(code) {
149 Some(code) => code,
150 None => return Err(()),
151 };
152
153 Ok(Self {
154 code,
155 msg: value.msg,
156 _extend: value._extend,
157 })
158 }
159}
160
161#[derive(
162 Debug,
163 PartialEq,
164 Eq,
165 Hash,
166 serde_repr::Deserialize_repr,
167 strum_macros::IntoStaticStr,
168 num_derive::FromPrimitive,
169)]
170#[repr(i32)]
171pub enum ErrorCode {
172 UnknownOrderSent = -2011,
173 OperationNotAllowed = 26,
174 ApiKeyRequired = 400,
175 NoAuthority = 401,
176 AccessDenied = 403,
177 TooManyRequests = 429,
178 InternalError = 500,
179 ServiceUnavailable = 503,
180 GatewayTimeout = 504,
181 SignatureVerificationFailed = 602,
182 UserDoesNotExist = 10001,
183 BadSymbol = 10007,
184 UserIdCannotBeNull = 10015,
185 InvalidAccessKey = 10072,
186 InvalidRequestTime = 10073,
187 AmountCannotBeNull = 10095,
188 AmountDecimalPlacesIsTooLong = 10096,
189 AmountIsError = 10097,
190 RiskControlSystemDetectedAbnormal = 10098,
191 UserSubAccountDoesNotOpen = 10099,
192 ThisCurrencyTransferIsNotSupported = 10100,
193 InsufficientBalance = 10101,
194 AmountCannotBeZeroOrNegative = 10102,
195 ThisAccountTransferIsNotSupported = 10103,
196 TransferOperationProcessing = 10200,
197 TransferInFailed = 10201,
198 TransferOutFailed = 10202,
199 TransferIsDisabled = 10206,
200 TransferIsForbidden = 10211,
201 ThisWithdrawalAddressIsNotOnTheCommonlyUsedAddressListOrHasBeenInvalidated = 10212,
202 NoAddressAvailablePleaseTryAgainLater = 10216,
203 AssetFlowWritingFailedPleaseTryAgain = 10219,
204 CurrencyCannotBeNull = 10222,
205 CurrencyDoesNotExist = 10232,
206 IntermediateAccountDoesNotConfiguredInRedisredis = 10259,
207 DueToRiskControlWithdrawalIsUnavailablePleaseTryAgainLater = 10265,
208 RemarkLengthIsTooLong = 10268,
209 SubsystemIsNotSupported = 20001,
210 InternalSystemErrorPleaseContactSupport = 20002,
211 RecordDoesNotExist = 22222,
212 SuspendedTransactionForTheSymbol = 30000,
213 TheCurrentTransactionDirectionIsNotAllowedToPlaceAnOrder = 30001,
214 TheMinimumTransactionVolumeCannotBeLessThan = 30002,
215 TheMaximumTransactionVolumeCannotBeGreaterThan = 30003,
216 InsufficientPosition = 30004,
217 Oversold = 30005,
218 NoValidTradePrice = 30010,
219 InvalidSymbol = 30014,
220 TradingDisabled = 30016,
221 MarketOrderIsDisabled = 30018,
222 ApiMarketOrderIsDisabled = 30019,
223 NoPermissionForTheSymbol = 30020,
224 NoExistOpponentOrder = 30025,
225 InvalidOrderIds = 30026,
226 TheCurrencyHasReachedTheMaximumPositionLimitTheBuyingIsSuspended = 30027,
227 TheCurrencyTriggeredThePlatformRiskControlTheSellingIsSuspended = 30028,
228 CannotExceedTheMaximumOrderLimit = 30029,
229 CannotExceedTheMaximumPosition = 30032,
230 CurrentOrderTypeCanNotPlaceOrder = 30041,
231 ParamIsError = 33333,
232 ParamCannotBeNull = 44444,
233 YourAccountIsAbnormal = 60005,
234 PairUserBanTradeApikey = 70011,
235 ApiKeyFormatInvalid = 700001,
236 SignatureForThisRequestIsNotValid = 700002,
237 TimestampForThisRequestIsOutsideOfTheRecvWindow = 700003,
238 ParamOrigClientOrderIdOrOrderIdMustBeSentButBothWereEmptyNull = 700004,
239 RecvWindowMustLessThan60000 = 700005,
240 IpNonWhiteList = 700006,
241 NoPermissionToAccessTheEndpoint = 700007,
242 IllegalCharactersFoundInParameter = 700008,
243 RequestFailedPleaseContactTheCustomerService = 730000,
246 PairNotFoundOrUserInformationError = 730001,
248 YourInputParamIsInvalidOrParameterError = 730002,
250 UnsupportedOperationPleaseContactTheCustomerService = 730003,
251 UnusualUserStatus = 730100,
252 SubAccountNameCannotBeNull = 730600,
253 SubAccountNameMustBeACombinationOf8To32LettersAndNumbers = 730601,
254 SubAccountRemarksCannotBeNull = 730602,
255 ApiKeyRemarksCannotBeNull = 730700,
256 ApiKeyPermissionCannotBeNull = 730701,
257 ApiKeyPermissionDoesNotExist = 730702,
258 TheIpInformationIsIncorrectAndAMaximumOf10IPsAreAllowedToBeBoundOnly = 730703,
259 TheBoundIpFormatIsIncorrectPleaseRefill = 730704,
260 AtMost30GroupsOfApiKeysAreAllowedToBeCreatedOnly = 730705,
261 ApiKeyInformationDoesNotExist = 730706,
262 AccessKeyCannotBeNull = 730707,
263 UserNameAlreadyExists = 730101,
264 SubAccountDoesNotExist = 140001,
265 SubAccountIsForbidden = 140002,
266 OrderDoesNotExist = -2013,
267 InvalidResponse = -1234568,
268}
269
270impl Display for ErrorCode {
271 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
272 let s: &'static str = self.into();
273 write!(f, "{}", s)
274 }
275}
276
277impl std::error::Error for ErrorCode {}
278
279impl Display for ErrorResponse {
280 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
281 write!(f, "Error {}: {}", self.code, self.msg)?;
282 if let Some(extend) = &self._extend {
283 write!(f, " (extend: {:?})", extend)?;
284 }
285
286 Ok(())
287 }
288}
289
290impl std::error::Error for ErrorResponse {}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295
296 #[test]
297 fn deserialize_error_parameter_error() {
298 let json = r#"
299 {"code":"730002","msg":"Parameter error"}
300 "#;
301 let response = serde_json::from_str::<ApiResponse<()>>(json).unwrap();
302 eprintln!("{:#?}", response);
303 let result = response.into_result();
304 eprintln!("{:#?}", &result);
305 assert!(result.is_err());
306 let err = result.unwrap_err();
307 assert_eq!(err.code, ErrorCode::YourInputParamIsInvalidOrParameterError);
308 }
309}