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