1use std::{num::ParseIntError, string::FromUtf8Error};
7use thiserror::Error;
8
9use crate::market_data::historical::HistoricalParseError;
10use crate::messages::{ResponseMessage, CODE_INDEX, MESSAGE_INDEX};
11use crate::orders::builder::ValidationError;
12
13#[derive(Debug, Error)]
18#[non_exhaustive]
19pub enum Error {
20 #[error(transparent)]
23 Io(#[from] std::io::Error),
24
25 #[error(transparent)]
27 ParseInt(#[from] ParseIntError),
28
29 #[error(transparent)]
31 FromUtf8(#[from] FromUtf8Error),
32
33 #[error(transparent)]
35 ParseTime(#[from] time::error::Parse),
36
37 #[error("{0}")]
39 Poison(String),
40
41 #[error("not implemented")]
44 NotImplemented,
45
46 #[error("parse error: {0} - {1} - {2}")]
49 Parse(usize, String, String),
50
51 #[error("server version {0} required, got {1}: {2}")]
54 ServerVersion(i32, i32, String),
55
56 #[error("error occurred: {0}")]
58 Simple(String),
59
60 #[error("InvalidArgument: {0}")]
62 InvalidArgument(String),
63
64 #[error("ConnectionFailed")]
66 ConnectionFailed,
67
68 #[error("ConnectionReset")]
70 ConnectionReset,
71
72 #[error("Cancelled")]
74 Cancelled,
75
76 #[error("Shutdown")]
78 Shutdown,
79
80 #[error("EndOfStream")]
82 EndOfStream,
83
84 #[error("UnexpectedResponse: {0:?}")]
86 UnexpectedResponse(ResponseMessage),
87
88 #[error("UnexpectedEndOfStream")]
90 UnexpectedEndOfStream,
91
92 #[error("[{0}] {1}")]
95 Message(i32, String),
96
97 #[error("AlreadySubscribed")]
99 AlreadySubscribed,
100
101 #[error("HistoricalParseError: {0}")]
103 HistoricalParseError(HistoricalParseError),
104}
105
106impl From<ResponseMessage> for Error {
107 fn from(err: ResponseMessage) -> Error {
108 let code = err.peek_int(CODE_INDEX).unwrap();
109 let message = err.peek_string(MESSAGE_INDEX);
110 Error::Message(code, message)
111 }
112}
113
114impl<T> From<std::sync::PoisonError<T>> for Error {
115 fn from(err: std::sync::PoisonError<T>) -> Error {
116 Error::Poison(format!("Mutex poison error: {err}"))
117 }
118}
119
120impl From<ValidationError> for Error {
121 fn from(err: ValidationError) -> Self {
122 match err {
123 ValidationError::InvalidQuantity(q) => Error::InvalidArgument(format!("Invalid quantity: {}", q)),
124 ValidationError::InvalidPrice(p) => Error::InvalidArgument(format!("Invalid price: {}", p)),
125 ValidationError::MissingRequiredField(field) => Error::InvalidArgument(format!("Missing required field: {}", field)),
126 ValidationError::InvalidCombination(msg) => Error::InvalidArgument(format!("Invalid combination: {}", msg)),
127 ValidationError::InvalidStopPrice { stop, current } => {
128 Error::InvalidArgument(format!("Invalid stop price {} for current price {}", stop, current))
129 }
130 ValidationError::InvalidLimitPrice { limit, current } => {
131 Error::InvalidArgument(format!("Invalid limit price {} for current price {}", limit, current))
132 }
133 ValidationError::InvalidBracketOrder(msg) => Error::InvalidArgument(format!("Invalid bracket order: {}", msg)),
134 ValidationError::InvalidPercentage { field, value, min, max } => {
135 Error::InvalidArgument(format!("Invalid {}: {} (must be between {} and {})", field, value, min, max))
136 }
137 }
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use std::error::Error as StdError;
145 use std::io;
146 use std::sync::{Mutex, PoisonError};
147 use time::macros::format_description;
148 use time::Time;
149
150 #[test]
151 fn test_error_debug() {
152 let error = Error::Simple("test error".to_string());
153 assert_eq!(format!("{error:?}"), "Simple(\"test error\")");
154 }
155
156 #[test]
157 fn test_error_display() {
158 let cases = vec![
159 (Error::Io(io::Error::new(io::ErrorKind::NotFound, "file not found")), "file not found"),
160 (Error::ParseInt("123x".parse::<i32>().unwrap_err()), "invalid digit found in string"),
161 (
162 Error::FromUtf8(String::from_utf8(vec![0, 159, 146, 150]).unwrap_err()),
163 "invalid utf-8 sequence of 1 bytes from index 1",
164 ),
165 (
166 Error::ParseTime(Time::parse("2021-13-01", format_description!("[year]-[month]-[day]")).unwrap_err()),
167 "the 'month' component could not be parsed",
168 ),
169 (Error::Poison("test poison".to_string()), "test poison"),
170 (Error::NotImplemented, "not implemented"),
171 (
172 Error::Parse(1, "value".to_string(), "message".to_string()),
173 "parse error: 1 - value - message",
174 ),
175 (
176 Error::ServerVersion(2, 1, "old version".to_string()),
177 "server version 2 required, got 1: old version",
178 ),
179 (Error::ConnectionFailed, "ConnectionFailed"),
180 (Error::Cancelled, "Cancelled"),
181 (Error::Simple("simple error".to_string()), "error occurred: simple error"),
182 ];
183
184 for (error, expected) in cases {
185 assert_eq!(error.to_string(), expected);
186 }
187 }
188
189 #[test]
190 fn test_error_is_error() {
191 let error = Error::Simple("test error".to_string());
192 assert!(error.source().is_none());
195 }
196
197 #[test]
198 fn test_from_io_error() {
199 let io_error = io::Error::other("io error");
200 let error: Error = io_error.into();
201 assert!(matches!(error, Error::Io(_)));
202 }
203
204 #[test]
205 fn test_from_parse_int_error() {
206 let parse_error = "abc".parse::<i32>().unwrap_err();
207 let error: Error = parse_error.into();
208 assert!(matches!(error, Error::ParseInt(_)));
209 }
210
211 #[test]
212 fn test_from_utf8_error() {
213 let utf8_error = String::from_utf8(vec![0, 159, 146, 150]).unwrap_err();
214 let error: Error = utf8_error.into();
215 assert!(matches!(error, Error::FromUtf8(_)));
216 }
217
218 #[test]
219 fn test_from_parse_time_error() {
220 let time_error = Time::parse("2021-13-01", format_description!("[year]-[month]-[day]")).unwrap_err();
221 let error: Error = time_error.into();
222 assert!(matches!(error, Error::ParseTime(_)));
223 }
224
225 #[test]
226 fn test_from_poison_error() {
227 let mutex = Mutex::new(());
228 let poison_error = PoisonError::new(mutex);
229 let error: Error = poison_error.into();
230 assert!(matches!(error, Error::Poison(_)));
231 }
232
233 #[test]
234 fn test_non_exhaustive() {
235 fn assert_non_exhaustive<T: StdError>() {}
236 assert_non_exhaustive::<Error>();
237 }
238}