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("Request canceled: {0}")]
84 Canceled(String),
85 #[error("Network error: {0}")]
87 NetworkError(String),
88 #[error("Unexpected HTTP status code {status}: {body}")]
90 UnexpectedStatus { status: StatusCode, body: String },
91}
92
93impl From<HttpClientError> for BitmexHttpError {
94 fn from(error: HttpClientError) -> Self {
95 Self::NetworkError(error.to_string())
96 }
97}
98
99impl From<String> for BitmexHttpError {
100 fn from(error: String) -> Self {
101 Self::ValidationError(error)
102 }
103}
104
105impl From<serde_json::Error> for BitmexHttpError {
108 fn from(error: serde_json::Error) -> Self {
109 Self::JsonError(error.to_string())
110 }
111}
112
113impl From<BitmexErrorResponse> for BitmexHttpError {
114 fn from(error: BitmexErrorResponse) -> Self {
115 Self::BitmexError {
116 error_name: error.error.name,
117 message: error.error.message,
118 }
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use rstest::rstest;
125
126 use super::*;
127 use crate::common::testing::load_test_json;
128
129 #[rstest]
130 fn test_bitmex_build_error_display() {
131 let error = BitmexBuildError::MissingSymbol;
132 assert_eq!(error.to_string(), "Missing required symbol");
133
134 let error = BitmexBuildError::InvalidCount;
135 assert_eq!(
136 error.to_string(),
137 "Invalid count: must be between 1 and 500"
138 );
139
140 let error = BitmexBuildError::InvalidTimeRange {
141 start_time: 100,
142 end_time: 50,
143 };
144 assert_eq!(
145 error.to_string(),
146 "Invalid time range: start_time (100) must be less than end_time (50)"
147 );
148 }
149
150 #[rstest]
151 fn test_bitmex_error_response_from_json() {
152 let json = load_test_json("http_error_response.json");
153
154 let error_response: BitmexErrorResponse = serde_json::from_str(&json).unwrap();
155 assert_eq!(error_response.error.message, "Invalid API Key.");
156 assert_eq!(error_response.error.name, "HTTPError");
157 }
158
159 #[rstest]
160 fn test_bitmex_http_error_from_error_response() {
161 let error_response = BitmexErrorResponse {
162 error: BitmexErrorMessage {
163 message: "Rate limit exceeded".to_string(),
164 name: "RateLimitError".to_string(),
165 },
166 };
167
168 let http_error: BitmexHttpError = error_response.into();
169 assert_eq!(
170 http_error.to_string(),
171 "BitMEX error RateLimitError: Rate limit exceeded"
172 );
173 }
174
175 #[rstest]
176 fn test_bitmex_http_error_from_json_error() {
177 let json_err = serde_json::from_str::<BitmexErrorResponse>("invalid json").unwrap_err();
178 let http_error: BitmexHttpError = json_err.into();
179 assert!(http_error.to_string().contains("JSON error"));
180 }
181
182 #[rstest]
183 fn test_bitmex_http_error_from_string() {
184 let error_msg = "Invalid parameter value".to_string();
185 let http_error: BitmexHttpError = error_msg.into();
186 assert_eq!(
187 http_error.to_string(),
188 "Parameter validation error: Invalid parameter value"
189 );
190 }
191
192 #[rstest]
193 fn test_unexpected_status_error() {
194 let error = BitmexHttpError::UnexpectedStatus {
195 status: StatusCode::BAD_GATEWAY,
196 body: "Server error".to_string(),
197 };
198 assert_eq!(
199 error.to_string(),
200 "Unexpected HTTP status code 502 Bad Gateway: Server error"
201 );
202 }
203}