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 use crate::common::testing::load_test_json;
125
126 #[rstest]
127 fn test_bitmex_build_error_display() {
128 let error = BitmexBuildError::MissingSymbol;
129 assert_eq!(error.to_string(), "Missing required symbol");
130
131 let error = BitmexBuildError::InvalidCount;
132 assert_eq!(
133 error.to_string(),
134 "Invalid count: must be between 1 and 500"
135 );
136
137 let error = BitmexBuildError::InvalidTimeRange {
138 start_time: 100,
139 end_time: 50,
140 };
141 assert_eq!(
142 error.to_string(),
143 "Invalid time range: start_time (100) must be less than end_time (50)"
144 );
145 }
146
147 #[rstest]
148 fn test_bitmex_error_response_from_json() {
149 let json = load_test_json("http_error_response.json");
150
151 let error_response: BitmexErrorResponse = serde_json::from_str(&json).unwrap();
152 assert_eq!(error_response.error.message, "Invalid API Key.");
153 assert_eq!(error_response.error.name, "HTTPError");
154 }
155
156 #[rstest]
157 fn test_bitmex_http_error_from_error_response() {
158 let error_response = BitmexErrorResponse {
159 error: BitmexErrorMessage {
160 message: "Rate limit exceeded".to_string(),
161 name: "RateLimitError".to_string(),
162 },
163 };
164
165 let http_error: BitmexHttpError = error_response.into();
166 assert_eq!(
167 http_error.to_string(),
168 "BitMEX error RateLimitError: Rate limit exceeded"
169 );
170 }
171
172 #[rstest]
173 fn test_bitmex_http_error_from_json_error() {
174 let json_err = serde_json::from_str::<BitmexErrorResponse>("invalid json").unwrap_err();
175 let http_error: BitmexHttpError = json_err.into();
176 assert!(http_error.to_string().contains("JSON error"));
177 }
178
179 #[rstest]
180 fn test_bitmex_http_error_from_string() {
181 let error_msg = "Invalid parameter value".to_string();
182 let http_error: BitmexHttpError = error_msg.into();
183 assert_eq!(
184 http_error.to_string(),
185 "Parameter validation error: Invalid parameter value"
186 );
187 }
188
189 #[rstest]
190 fn test_unexpected_status_error() {
191 let error = BitmexHttpError::UnexpectedStatus {
192 status: StatusCode::BAD_GATEWAY,
193 body: "Server error".to_string(),
194 };
195 assert_eq!(
196 error.to_string(),
197 "Unexpected HTTP status code 502 Bad Gateway: Server error"
198 );
199 }
200}