1use rusty_common::SmartString;
2use std::fmt::Write;
3use thiserror::Error;
4
5pub mod batch_errors;
6pub mod exchange_errors;
8
9#[derive(Error, Debug)]
11pub enum EMSError {
12 #[error("Connection error: {0}")]
14 ConnectionError(SmartString),
15
16 #[error("Authentication error: {0}")]
18 AuthenticationError(SmartString),
19
20 #[error("Order submission error: {0}")]
22 OrderSubmissionError(SmartString),
23
24 #[error("Order cancellation error: {0}")]
26 OrderCancellationError(SmartString),
27
28 #[error("Order modification error: {0}")]
30 OrderModificationError(SmartString),
31
32 #[error("Rate limit exceeded: {message}")]
34 RateLimitExceeded {
35 message: SmartString,
37 retry_after_ms: Option<u64>,
39 },
40
41 #[error("Invalid order parameters: {0}")]
43 InvalidOrderParameters(SmartString),
44
45 #[error("Insufficient balance: {0}")]
47 InsufficientBalance(SmartString),
48
49 #[error("Instrument not found: {0}")]
51 InstrumentNotFound(SmartString),
52
53 #[error("Exchange API error ({exchange}): {code} - {message}")]
55 ExchangeApiError {
56 exchange: SmartString,
58 code: i32,
60 message: SmartString,
62 details: Option<SmartString>,
64 },
65
66 #[error("{exchange} does not support {operation}")]
68 OperationNotSupported {
69 exchange: SmartString,
71 operation: SmartString,
73 },
74
75 #[error("Unknown error: {0}")]
77 UnknownError(SmartString),
78
79 #[error("JSON error: {0}")]
81 JsonError(#[from] simd_json::Error),
82
83 #[error("JSON parsing error: {context} - {message}")]
85 JsonParsingError {
86 context: SmartString,
88 message: SmartString,
90 },
91
92 #[error("I/O error: {0}")]
94 IoError(#[from] std::io::Error),
95
96 #[error("Network error: {0}")]
98 NetworkError(SmartString),
99
100 #[error("Timeout after {duration_ms}ms: {context}")]
102 Timeout {
103 duration_ms: u64,
105 context: SmartString,
107 },
108
109 #[error("WebSocket error: {0}")]
111 WebSocketError(SmartString),
112
113 #[error("WebSocket frame error: {0}")]
115 WebSocketFrameError(String),
116}
117
118pub type Result<T> = std::result::Result<T, EMSError>;
120
121impl<T> From<flume::SendError<T>> for EMSError {
123 fn from(err: flume::SendError<T>) -> Self {
124 Self::channel_send_error(&err)
125 }
126}
127
128pub trait AnyhowToEmsError<T> {
130 fn to_ems_error(self) -> Result<T>;
132}
133
134impl<T> AnyhowToEmsError<T> for anyhow::Result<T> {
135 fn to_ems_error(self) -> Result<T> {
136 self.map_err(|e| EMSError::UnknownError(e.to_string().into()))
137 }
138}
139
140impl EMSError {
142 pub fn connection<S: Into<SmartString>>(msg: S) -> Self {
144 Self::ConnectionError(msg.into())
145 }
146
147 pub fn auth<S: Into<SmartString>>(msg: S) -> Self {
149 Self::AuthenticationError(msg.into())
150 }
151
152 pub fn order_submission<S: Into<SmartString>>(msg: S) -> Self {
154 Self::OrderSubmissionError(msg.into())
155 }
156
157 pub fn order_cancellation<S: Into<SmartString>>(msg: S) -> Self {
159 Self::OrderCancellationError(msg.into())
160 }
161
162 pub fn order_modification<S: Into<SmartString>>(msg: S) -> Self {
164 Self::OrderModificationError(msg.into())
165 }
166
167 pub fn rate_limit<S: Into<SmartString>>(msg: S, retry_after_ms: Option<u64>) -> Self {
169 Self::RateLimitExceeded {
170 message: msg.into(),
171 retry_after_ms,
172 }
173 }
174
175 pub fn invalid_params<S: Into<SmartString>>(msg: S) -> Self {
177 Self::InvalidOrderParameters(msg.into())
178 }
179
180 pub fn insufficient_balance<S: Into<SmartString>>(msg: S) -> Self {
182 Self::InsufficientBalance(msg.into())
183 }
184
185 pub fn instrument_not_found<S: Into<SmartString>>(symbol: S) -> Self {
187 Self::InstrumentNotFound(symbol.into())
188 }
189
190 pub fn exchange_api<S1, S2, S3>(
192 exchange: S1,
193 code: i32,
194 message: S2,
195 details: Option<S3>,
196 ) -> Self
197 where
198 S1: Into<SmartString>,
199 S2: Into<SmartString>,
200 S3: Into<SmartString>,
201 {
202 Self::ExchangeApiError {
203 exchange: exchange.into(),
204 code,
205 message: message.into(),
206 details: details.map(std::convert::Into::into),
207 }
208 }
209
210 pub fn not_supported<S1, S2>(exchange: S1, operation: S2) -> Self
212 where
213 S1: Into<SmartString>,
214 S2: Into<SmartString>,
215 {
216 Self::OperationNotSupported {
217 exchange: exchange.into(),
218 operation: operation.into(),
219 }
220 }
221
222 pub fn websocket<S: Into<SmartString>>(msg: S) -> Self {
224 Self::WebSocketError(msg.into())
225 }
226
227 pub fn timeout<S: Into<SmartString>>(duration_ms: u64, context: S) -> Self {
229 Self::Timeout {
230 duration_ms,
231 context: context.into(),
232 }
233 }
234
235 pub fn json_parse<S1, S2>(context: S1, message: S2) -> Self
237 where
238 S1: Into<SmartString>,
239 S2: Into<SmartString>,
240 {
241 Self::JsonParsingError {
242 context: context.into(),
243 message: message.into(),
244 }
245 }
246
247 pub fn internal<S: Into<SmartString>>(msg: S) -> Self {
249 Self::UnknownError(msg.into())
250 }
251
252 pub fn channel_send_error(err: &impl std::fmt::Display) -> Self {
256 let mut msg = SmartString::new();
257 write!(msg, "Channel send error: {err}").expect("Failed to format channel send error");
259 Self::UnknownError(msg)
260 }
261
262 pub fn json_error(err: &impl std::fmt::Display) -> Self {
264 let mut msg = SmartString::new();
265 write!(msg, "JSON error: {err}").expect("Failed to format JSON error");
266 Self::UnknownError(msg)
267 }
268
269 pub fn connection_failed(err: &impl std::fmt::Display) -> Self {
271 let mut msg = SmartString::new();
272 write!(msg, "Connection failed: {err}").expect("Failed to format connection error");
273 Self::ConnectionError(msg)
274 }
275
276 #[must_use]
278 pub fn http_status_error(status: u16) -> SmartString {
279 let mut msg = SmartString::new();
280 write!(msg, "HTTP {status}").expect("Failed to format HTTP status error");
281 msg
282 }
283
284 #[must_use]
286 pub fn rate_limit_with_exchange(exchange: &str, msg: &str) -> SmartString {
287 let mut message = SmartString::new();
288 write!(message, "{exchange} rate limit exceeded: {msg}")
289 .expect("Failed to format rate limit error");
290 message
291 }
292
293 #[must_use]
295 pub fn auth_failed_error(exchange: &str) -> Self {
296 let mut msg = SmartString::new();
297 write!(msg, "{exchange} authentication failed").expect("Failed to format auth error");
298 Self::AuthenticationError(msg)
299 }
300
301 #[must_use]
303 pub fn access_forbidden_error(exchange: &str) -> Self {
304 let mut msg = SmartString::new();
305 write!(msg, "{exchange} access forbidden")
306 .expect("Failed to format access forbidden error");
307 Self::AuthenticationError(msg)
308 }
309
310 #[must_use]
312 pub fn rate_limit_exceeded_error(exchange: &str) -> SmartString {
313 let mut msg = SmartString::new();
314 write!(msg, "{exchange} rate limit exceeded")
315 .expect("Failed to format rate limit exceeded error");
316 msg
317 }
318
319 #[must_use]
321 pub fn internal_server_error(exchange: &str) -> Self {
322 let mut msg = SmartString::new();
323 write!(msg, "{exchange} internal server error")
324 .expect("Failed to format internal server error");
325 Self::ConnectionError(msg)
326 }
327
328 #[must_use]
330 pub fn service_unavailable_error(exchange: &str) -> Self {
331 let mut msg = SmartString::new();
332 write!(msg, "{exchange} service unavailable")
333 .expect("Failed to format service unavailable error");
334 Self::ConnectionError(msg)
335 }
336
337 #[must_use]
339 pub fn gateway_timeout_error(exchange: &str) -> SmartString {
340 let mut msg = SmartString::new();
341 write!(msg, "{exchange} gateway timeout").expect("Failed to format gateway timeout error");
342 msg
343 }
344
345 pub fn auth_exchange_failed(exchange: &str, err: &impl std::fmt::Display) -> Self {
348 let mut msg = SmartString::new();
349 write!(msg, "{exchange} auth failed: {err}").expect("Failed to format auth exchange error");
350 Self::AuthenticationError(msg)
351 }
352
353 pub fn auth_invalid_header_name(err: &impl std::fmt::Display) -> Self {
355 let mut msg = SmartString::new();
356 write!(msg, "Invalid header name: {err}")
357 .expect("Failed to format invalid header name error");
358 Self::AuthenticationError(msg)
359 }
360
361 pub fn auth_invalid_header_value(err: &impl std::fmt::Display) -> Self {
363 let mut msg = SmartString::new();
364 write!(msg, "Invalid header value: {err}")
365 .expect("Failed to format invalid header value error");
366 Self::AuthenticationError(msg)
367 }
368
369 #[must_use]
371 pub const fn is_recoverable(&self) -> bool {
372 matches!(
373 self,
374 Self::ConnectionError(_)
375 | Self::RateLimitExceeded { .. }
376 | Self::Timeout { .. }
377 | Self::NetworkError(_)
378 | Self::WebSocketError(_)
379 )
380 }
381
382 #[must_use]
384 pub const fn retry_delay_ms(&self) -> Option<u64> {
385 match self {
386 Self::RateLimitExceeded { retry_after_ms, .. } => *retry_after_ms,
387 Self::Timeout { .. } => Some(5000), Self::ConnectionError(_) | Self::NetworkError(_) => Some(1000), _ => None,
390 }
391 }
392}
393
394impl Clone for EMSError {
396 fn clone(&self) -> Self {
397 match self {
398 Self::ConnectionError(s) => Self::ConnectionError(s.clone()),
399 Self::AuthenticationError(s) => Self::AuthenticationError(s.clone()),
400 Self::OrderSubmissionError(s) => Self::OrderSubmissionError(s.clone()),
401 Self::OrderCancellationError(s) => Self::OrderCancellationError(s.clone()),
402 Self::OrderModificationError(s) => Self::OrderModificationError(s.clone()),
403 Self::RateLimitExceeded {
404 message,
405 retry_after_ms,
406 } => Self::RateLimitExceeded {
407 message: message.clone(),
408 retry_after_ms: *retry_after_ms,
409 },
410 Self::InvalidOrderParameters(s) => Self::InvalidOrderParameters(s.clone()),
411 Self::InsufficientBalance(s) => Self::InsufficientBalance(s.clone()),
412 Self::InstrumentNotFound(s) => Self::InstrumentNotFound(s.clone()),
413 Self::ExchangeApiError {
414 exchange,
415 code,
416 message,
417 details,
418 } => Self::ExchangeApiError {
419 exchange: exchange.clone(),
420 code: *code,
421 message: message.clone(),
422 details: details.clone(),
423 },
424 Self::OperationNotSupported {
425 exchange,
426 operation,
427 } => Self::OperationNotSupported {
428 exchange: exchange.clone(),
429 operation: operation.clone(),
430 },
431 Self::UnknownError(s) => Self::UnknownError(s.clone()),
432 Self::JsonError(e) => Self::json_error(e),
433 Self::JsonParsingError { context, message } => Self::JsonParsingError {
434 context: context.clone(),
435 message: message.clone(),
436 },
437 Self::IoError(e) => Self::IoError(std::io::Error::new(e.kind(), e.to_string())),
438 Self::NetworkError(s) => Self::NetworkError(s.clone()),
439 Self::Timeout {
440 duration_ms,
441 context,
442 } => Self::Timeout {
443 duration_ms: *duration_ms,
444 context: context.clone(),
445 },
446 Self::WebSocketError(s) => Self::WebSocketError(s.clone()),
447 Self::WebSocketFrameError(s) => Self::WebSocketFrameError(s.clone()),
448 }
449 }
450}
451
452impl From<rusty_common::CommonError> for EMSError {
454 fn from(err: rusty_common::CommonError) -> Self {
455 Self::UnknownError(err.to_string().into())
456 }
457}
458
459impl From<anyhow::Error> for EMSError {
461 fn from(err: anyhow::Error) -> Self {
462 if let Some(io_err) = err.downcast_ref::<std::io::Error>() {
464 return Self::IoError(io_err.kind().into());
465 }
466
467 if let Some(req_err) = err.downcast_ref::<reqwest::Error>() {
468 if req_err.is_timeout() {
469 return Self::Timeout {
470 duration_ms: 30000, context: "Request timed out".into(),
472 };
473 }
474 return Self::NetworkError(req_err.to_string().into());
476 }
477
478 Self::UnknownError(err.to_string().into())
480 }
481}
482
483impl From<reqwest::Error> for EMSError {
485 fn from(err: reqwest::Error) -> Self {
486 if err.is_timeout() {
487 Self::Timeout {
488 duration_ms: 30000, context: "Request timed out".into(),
490 }
491 } else if err.is_connect() {
492 Self::connection_failed(&err)
493 } else if err.is_status() {
494 if let Some(status) = err.status() {
495 Self::ExchangeApiError {
496 exchange: "Unknown".into(),
497 code: i32::from(status.as_u16()),
498 message: Self::http_status_error(status.as_u16()),
499 details: Some(err.to_string().into()),
500 }
501 } else {
502 Self::NetworkError(err.to_string().into())
503 }
504 } else {
505 Self::NetworkError(err.to_string().into())
506 }
507 }
508}
509
510#[macro_export]
512macro_rules! exchange_error {
513 ($exchange:expr, $code:expr, $msg:expr) => {
514 EMSError::exchange_api($exchange, $code, $msg, None::<String>)
515 };
516 ($exchange:expr, $code:expr, $msg:expr, $details:expr) => {
517 EMSError::exchange_api($exchange, $code, $msg, Some($details))
518 };
519}
520
521#[macro_export]
523macro_rules! not_supported {
524 ($exchange:expr, $operation:expr) => {
525 EMSError::not_supported($exchange, $operation)
526 };
527}