nautilus_bybit/http/
error.rs1use nautilus_network::http::HttpClientError;
24use serde::{Deserialize, Serialize};
25use thiserror::Error;
26
27#[derive(Debug, Clone, Error)]
29pub enum BybitBuildError {
30 #[error("Missing required category")]
32 MissingCategory,
33 #[error("Missing required symbol")]
35 MissingSymbol,
36 #[error("Missing required interval")]
38 MissingInterval,
39 #[error("Invalid limit: must be between 1 and 1000")]
41 InvalidLimit,
42 #[error("Invalid time range: start ({start}) must be less than end ({end})")]
44 InvalidTimeRange { start: i64, end: i64 },
45 #[error("Cannot specify both 'orderId' and 'orderLinkId'")]
47 BothOrderIds,
48 #[error("Missing required order identifier (orderId or orderLinkId)")]
50 MissingOrderId,
51}
52
53#[derive(Clone, Debug, Deserialize, Serialize)]
58#[serde(rename_all = "camelCase")]
59pub struct BybitErrorResponse {
60 pub ret_code: i32,
62 pub ret_msg: String,
64 #[serde(default)]
66 pub ret_ext_info: Option<serde_json::Value>,
67}
68
69#[derive(Debug, Clone, Error)]
71pub enum BybitHttpError {
72 #[error("Missing credentials for authenticated request")]
74 MissingCredentials,
75 #[error("Bybit error {error_code}: {message}")]
77 BybitError { error_code: i32, message: String },
78 #[error("JSON error: {0}")]
80 JsonError(String),
81 #[error("Parameter validation error: {0}")]
83 ValidationError(String),
84 #[error("Build error: {0}")]
86 BuildError(#[from] BybitBuildError),
87 #[error("Network error: {0}")]
89 NetworkError(String),
90 #[error("Unexpected HTTP status code {status}: {body}")]
92 UnexpectedStatus { status: u16, body: String },
93}
94
95impl From<HttpClientError> for BybitHttpError {
96 fn from(error: HttpClientError) -> Self {
97 Self::NetworkError(error.to_string())
98 }
99}
100
101impl From<String> for BybitHttpError {
102 fn from(error: String) -> Self {
103 Self::ValidationError(error)
104 }
105}
106
107impl From<serde_json::Error> for BybitHttpError {
110 fn from(error: serde_json::Error) -> Self {
111 Self::JsonError(error.to_string())
112 }
113}
114
115impl From<BybitErrorResponse> for BybitHttpError {
116 fn from(error: BybitErrorResponse) -> Self {
117 Self::BybitError {
118 error_code: error.ret_code,
119 message: error.ret_msg,
120 }
121 }
122}
123
124#[cfg(test)]
129mod tests {
130 use rstest::rstest;
131
132 use super::*;
133
134 #[rstest]
135 fn test_bybit_build_error_display() {
136 let error = BybitBuildError::MissingSymbol;
137 assert_eq!(error.to_string(), "Missing required symbol");
138
139 let error = BybitBuildError::InvalidLimit;
140 assert_eq!(
141 error.to_string(),
142 "Invalid limit: must be between 1 and 1000"
143 );
144
145 let error = BybitBuildError::InvalidTimeRange {
146 start: 100,
147 end: 50,
148 };
149 assert_eq!(
150 error.to_string(),
151 "Invalid time range: start (100) must be less than end (50)"
152 );
153 }
154
155 #[rstest]
156 fn test_bybit_http_error_from_error_response() {
157 let error_response = BybitErrorResponse {
158 ret_code: 10001,
159 ret_msg: "Parameter error".to_string(),
160 ret_ext_info: None,
161 };
162
163 let http_error: BybitHttpError = error_response.into();
164 assert_eq!(http_error.to_string(), "Bybit error 10001: Parameter error");
165 }
166
167 #[rstest]
168 fn test_bybit_http_error_from_json_error() {
169 let json_err = serde_json::from_str::<BybitErrorResponse>("invalid json").unwrap_err();
170 let http_error: BybitHttpError = json_err.into();
171 assert!(http_error.to_string().contains("JSON error"));
172 }
173
174 #[rstest]
175 fn test_bybit_http_error_from_string() {
176 let error_msg = "Invalid parameter value".to_string();
177 let http_error: BybitHttpError = error_msg.into();
178 assert_eq!(
179 http_error.to_string(),
180 "Parameter validation error: Invalid parameter value"
181 );
182 }
183
184 #[rstest]
185 fn test_unexpected_status_error() {
186 let error = BybitHttpError::UnexpectedStatus {
187 status: 502,
188 body: "Server error".to_string(),
189 };
190 assert_eq!(
191 error.to_string(),
192 "Unexpected HTTP status code 502: Server error"
193 );
194 }
195}