nautilus_bitmex/http/
error.rs1use nautilus_network::http::{HttpClientError, StatusCode};
19use serde::{Deserialize, Serialize};
20use thiserror::Error;
21
22#[derive(Debug, Clone, Error)]
24pub enum BitmexBuildError {
25 #[error("Missing required symbol")]
27 MissingSymbol,
28 #[error("Invalid count: must be between 1 and 500")]
30 InvalidCount,
31 #[error("Invalid start: must be non-negative")]
33 InvalidStart,
34 #[error(
36 "Invalid time range: start_time ({start_time}) must be less than end_time ({end_time})"
37 )]
38 InvalidTimeRange { start_time: i64, end_time: i64 },
39 #[error("Cannot specify both 'orderID' and 'clOrdID'")]
41 BothOrderIds,
42 #[error("Missing required order identifier (orderID or clOrdID)")]
44 MissingOrderId,
45}
46
47#[derive(Clone, Debug, Deserialize, Serialize)]
49pub struct BitmexErrorResponse {
50 pub error: BitmexErrorMessage,
52}
53
54#[derive(Clone, Debug, Deserialize, Serialize)]
56pub struct BitmexErrorMessage {
57 pub message: String,
59 pub name: String,
61}
62
63#[derive(Debug, Clone, Error)]
65pub enum BitmexHttpError {
66 #[error("Missing credentials for authenticated request")]
68 MissingCredentials,
69 #[error("BitMEX error {error_name}: {message}")]
71 BitmexError { error_name: String, message: String },
72 #[error("JSON error: {0}")]
74 JsonError(String),
75 #[error("Parameter validation error: {0}")]
77 ValidationError(String),
78 #[error("Build error: {0}")]
80 BuildError(#[from] BitmexBuildError),
81 #[error("Request canceled: {0}")]
83 Canceled(String),
84 #[error("Network error: {0}")]
86 NetworkError(String),
87 #[error("Unexpected HTTP status code {status}: {body}")]
89 UnexpectedStatus { status: StatusCode, body: String },
90}
91
92impl From<HttpClientError> for BitmexHttpError {
93 fn from(error: HttpClientError) -> Self {
94 Self::NetworkError(error.to_string())
95 }
96}
97
98impl From<String> for BitmexHttpError {
99 fn from(error: String) -> Self {
100 Self::ValidationError(error)
101 }
102}
103
104impl From<serde_json::Error> for BitmexHttpError {
107 fn from(error: serde_json::Error) -> Self {
108 Self::JsonError(error.to_string())
109 }
110}
111
112impl From<BitmexErrorResponse> for BitmexHttpError {
113 fn from(error: BitmexErrorResponse) -> Self {
114 Self::BitmexError {
115 error_name: error.error.name,
116 message: error.error.message,
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use rstest::rstest;
124
125 use super::*;
126 use crate::common::testing::load_test_json;
127
128 #[rstest]
129 fn test_bitmex_build_error_display() {
130 let error = BitmexBuildError::MissingSymbol;
131 assert_eq!(error.to_string(), "Missing required symbol");
132
133 let error = BitmexBuildError::InvalidCount;
134 assert_eq!(
135 error.to_string(),
136 "Invalid count: must be between 1 and 500"
137 );
138
139 let error = BitmexBuildError::InvalidTimeRange {
140 start_time: 100,
141 end_time: 50,
142 };
143 assert_eq!(
144 error.to_string(),
145 "Invalid time range: start_time (100) must be less than end_time (50)"
146 );
147 }
148
149 #[rstest]
150 fn test_bitmex_error_response_from_json() {
151 let json = load_test_json("http_error_response.json");
152
153 let error_response: BitmexErrorResponse = serde_json::from_str(&json).unwrap();
154 assert_eq!(error_response.error.message, "Invalid API Key.");
155 assert_eq!(error_response.error.name, "HTTPError");
156 }
157
158 #[rstest]
159 fn test_bitmex_http_error_from_error_response() {
160 let error_response = BitmexErrorResponse {
161 error: BitmexErrorMessage {
162 message: "Rate limit exceeded".to_string(),
163 name: "RateLimitError".to_string(),
164 },
165 };
166
167 let http_error: BitmexHttpError = error_response.into();
168 assert_eq!(
169 http_error.to_string(),
170 "BitMEX error RateLimitError: Rate limit exceeded"
171 );
172 }
173
174 #[rstest]
175 fn test_bitmex_http_error_from_json_error() {
176 let json_err = serde_json::from_str::<BitmexErrorResponse>("invalid json").unwrap_err();
177 let http_error: BitmexHttpError = json_err.into();
178 assert!(http_error.to_string().contains("JSON error"));
179 }
180
181 #[rstest]
182 fn test_bitmex_http_error_from_string() {
183 let error_msg = "Invalid parameter value".to_string();
184 let http_error: BitmexHttpError = error_msg.into();
185 assert_eq!(
186 http_error.to_string(),
187 "Parameter validation error: Invalid parameter value"
188 );
189 }
190
191 #[rstest]
192 fn test_unexpected_status_error() {
193 let error = BitmexHttpError::UnexpectedStatus {
194 status: StatusCode::BAD_GATEWAY,
195 body: "Server error".to_string(),
196 };
197 assert_eq!(
198 error.to_string(),
199 "Unexpected HTTP status code 502 Bad Gateway: Server error"
200 );
201 }
202}