nautilus_architect_ax/websocket/
error.rs1use serde::{Deserialize, Serialize};
19use thiserror::Error;
20use tokio_tungstenite::tungstenite;
21
22#[derive(Debug, Clone, Error)]
24pub enum AxWsError {
25 #[error("Parsing error: {0}")]
27 ParsingError(String),
28 #[error("Ax error: {0}")]
30 ApiError(String),
31 #[error("JSON error: {0}")]
33 JsonError(String),
34 #[error("Client error: {0}")]
36 ClientError(String),
37 #[error("Authentication error: {0}")]
39 AuthenticationError(String),
40 #[error("Connection error: {0}")]
42 ConnectionError(String),
43 #[error("Subscription error: {0}")]
45 SubscriptionError(String),
46 #[error("Order error: {0}")]
48 OrderError(String),
49 #[error("Tungstenite error: {0}")]
51 TungsteniteError(String),
52 #[error("Channel error: {0}")]
54 ChannelError(String),
55 #[error("Timeout: {0}")]
57 Timeout(String),
58}
59
60impl From<tungstenite::Error> for AxWsError {
61 fn from(error: tungstenite::Error) -> Self {
62 Self::TungsteniteError(error.to_string())
63 }
64}
65
66impl From<serde_json::Error> for AxWsError {
67 fn from(error: serde_json::Error) -> Self {
68 Self::JsonError(error.to_string())
69 }
70}
71
72impl From<String> for AxWsError {
73 fn from(msg: String) -> Self {
74 Self::ClientError(msg)
75 }
76}
77
78#[derive(Clone, Debug, Serialize, Deserialize)]
80pub struct AxWsErrorResponse {
81 #[serde(default)]
83 pub code: Option<String>,
84 #[serde(default)]
86 pub message: Option<String>,
87 #[serde(default)]
89 pub rid: Option<i64>,
90}
91
92impl From<AxWsErrorResponse> for AxWsError {
93 fn from(error: AxWsErrorResponse) -> Self {
94 let message = error
95 .message
96 .or(error.code)
97 .unwrap_or_else(|| "Unknown error".to_string());
98 Self::ApiError(message)
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use rstest::rstest;
105
106 use super::*;
107
108 #[rstest]
109 fn test_architect_ws_error_display() {
110 let error = AxWsError::ParsingError("invalid message format".to_string());
111 assert_eq!(error.to_string(), "Parsing error: invalid message format");
112
113 let error = AxWsError::ApiError("INSUFFICIENT_MARGIN".to_string());
114 assert_eq!(error.to_string(), "Ax error: INSUFFICIENT_MARGIN");
115
116 let error = AxWsError::AuthenticationError("token expired".to_string());
117 assert_eq!(error.to_string(), "Authentication error: token expired");
118 }
119
120 #[rstest]
121 fn test_architect_ws_error_from_json_error() {
122 let json_err = serde_json::from_str::<serde_json::Value>("invalid json")
123 .expect_err("Should fail to parse");
124 let ws_err = AxWsError::from(json_err);
125
126 assert!(matches!(ws_err, AxWsError::JsonError(_)));
127 }
128
129 #[rstest]
130 fn test_architect_ws_error_from_string() {
131 let error = AxWsError::from("Test client error".to_string());
132 assert_eq!(error.to_string(), "Client error: Test client error");
133 }
134
135 #[rstest]
136 fn test_architect_ws_error_response_to_error() {
137 let error_response = AxWsErrorResponse {
138 code: Some("ORDER_NOT_FOUND".to_string()),
139 message: Some("Order does not exist".to_string()),
140 rid: Some(123),
141 };
142
143 let ws_error = AxWsError::from(error_response);
144 assert_eq!(ws_error.to_string(), "Ax error: Order does not exist");
145 }
146
147 #[rstest]
148 fn test_architect_ws_error_response_fallback_to_code() {
149 let error_response = AxWsErrorResponse {
150 code: Some("INVALID_REQUEST".to_string()),
151 message: None,
152 rid: None,
153 };
154
155 let ws_error = AxWsError::from(error_response);
156 assert_eq!(ws_error.to_string(), "Ax error: INVALID_REQUEST");
157 }
158}