1use std::{borrow::Cow, fmt};
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
6#[non_exhaustive]
7pub(crate) enum ErrorType {
8 Default,
10
11 InvalidPrice,
13 InvalidPriceOrQuantity,
14 InvalidQuantity,
15 OrderAlredyExists,
16 OrderNotFound,
17 OrderPostOnly,
18 OrderIOC,
19 OrderFOK,
20
21 InsufficientQuantity,
23 InvalidPriceLevel,
24 OrderBookEmpty,
25}
26
27impl ErrorType {
28 pub(crate) fn code(self) -> u32 {
30 match self {
31 ErrorType::Default => 1000,
33
34 ErrorType::InvalidQuantity => 1101,
36 ErrorType::InvalidPrice => 1102,
37 ErrorType::InvalidPriceOrQuantity => 1103,
38 ErrorType::OrderPostOnly => 1104,
39 ErrorType::OrderIOC => 1105,
40 ErrorType::OrderFOK => 1106,
41 ErrorType::OrderAlredyExists => 1109,
42 ErrorType::OrderNotFound => 1110,
43
44 ErrorType::OrderBookEmpty => 1200,
46 ErrorType::InsufficientQuantity => 1201,
47 ErrorType::InvalidPriceLevel => 1202,
48 }
49 }
50
51 pub(crate) const fn message(self) -> &'static str {
53 match self {
54 ErrorType::Default => "Something wrong",
56
57 ErrorType::InvalidQuantity => "Invalid order quantity",
59 ErrorType::InvalidPrice => "Invalid order price",
60 ErrorType::InvalidPriceOrQuantity => "Invalid order price or quantity",
61 ErrorType::OrderPostOnly => {
62 "Post Only order rejected: would execute immediately against existing orders"
63 }
64 ErrorType::OrderIOC => {
65 "IOC order rejected: no immediate liquidity available at requested price"
66 }
67 ErrorType::OrderFOK => "FOK order rejected: unable to fill entire quantity immediately",
68 ErrorType::OrderAlredyExists => "Order already exists",
69 ErrorType::OrderNotFound => "Order not found",
70
71 ErrorType::OrderBookEmpty => "Order book is empty",
73 ErrorType::InsufficientQuantity => "Insufficient quantity to calculate price",
74 ErrorType::InvalidPriceLevel => "Invalid order price level",
75 }
76 }
77}
78
79#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
83#[non_exhaustive]
84pub struct OrderBookError {
85 pub code: u32,
86 pub message: String,
87}
88
89impl OrderBookError {
90 #[inline]
92 pub(crate) fn new(code: u32, message: impl Into<String>) -> Self {
93 Self { code, message: message.into() }
94 }
95
96 #[inline]
98 pub(crate) fn from_code(code: u32) -> Self {
99 let msg = default_message_for_code(code);
100 Self::new(code, msg)
101 }
102
103 #[inline]
105 pub(crate) fn from_message(message: impl Into<String>) -> Self {
106 Self::new(ErrorType::Default.code(), message)
107 }
108}
109
110impl fmt::Display for OrderBookError {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 write!(f, "[{}] {}", self.code, self.message)
113 }
114}
115
116#[inline]
119pub(crate) fn default_message_for_code(code: u32) -> Cow<'static, str> {
120 match code {
121 1000 => Cow::Borrowed(ErrorType::Default.message()),
123
124 1101 => Cow::Borrowed(ErrorType::InvalidQuantity.message()),
126 1102 => Cow::Borrowed(ErrorType::InvalidPrice.message()),
127 1103 => Cow::Borrowed(ErrorType::InvalidPriceOrQuantity.message()),
128 1104 => Cow::Borrowed(ErrorType::OrderPostOnly.message()),
129 1105 => Cow::Borrowed(ErrorType::OrderIOC.message()),
130 1106 => Cow::Borrowed(ErrorType::OrderFOK.message()),
131 1109 => Cow::Borrowed(ErrorType::OrderAlredyExists.message()),
132 1110 => Cow::Borrowed(ErrorType::OrderNotFound.message()),
133
134 1200 => Cow::Borrowed(ErrorType::InsufficientQuantity.message()),
136 1201 => Cow::Borrowed(ErrorType::InvalidPriceLevel.message()),
137
138 _ => Cow::Owned(format!("Unknown error ({code})")),
139 }
140}
141
142impl From<ErrorType> for OrderBookError {
144 #[inline]
145 fn from(t: ErrorType) -> Self {
146 Self::new(t.code(), t.message())
147 }
148}
149
150pub(crate) trait IntoOrderBookError {
152 fn into_error(self) -> OrderBookError;
153}
154
155impl IntoOrderBookError for ErrorType {
156 #[inline]
157 fn into_error(self) -> OrderBookError {
158 self.into()
159 }
160}
161
162impl IntoOrderBookError for u32 {
163 #[inline]
164 fn into_error(self) -> OrderBookError {
165 OrderBookError::from_code(self)
166 }
167}
168
169impl IntoOrderBookError for &str {
170 #[inline]
171 fn into_error(self) -> OrderBookError {
172 OrderBookError::from_message(self)
173 }
174}
175
176impl IntoOrderBookError for String {
177 #[inline]
178 fn into_error(self) -> OrderBookError {
179 OrderBookError::from_message(self)
180 }
181}
182
183#[inline]
185pub(crate) fn make_error<E: IntoOrderBookError>(e: E) -> OrderBookError {
186 e.into_error()
187}
188
189pub type Result<T> = std::result::Result<T, OrderBookError>;
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
197 fn test_error_type_codes_and_messages() {
198 let cases = vec![
199 (ErrorType::Default, 1000, "Something wrong"),
200 (ErrorType::InvalidQuantity, 1101, "Invalid order quantity"),
201 (ErrorType::InvalidPrice, 1102, "Invalid order price"),
202 (ErrorType::InvalidPriceOrQuantity, 1103, "Invalid order price or quantity"),
203 (
204 ErrorType::OrderPostOnly,
205 1104,
206 "Post Only order rejected: would execute immediately against existing orders",
207 ),
208 (
209 ErrorType::OrderIOC,
210 1105,
211 "IOC order rejected: no immediate liquidity available at requested price",
212 ),
213 (
214 ErrorType::OrderFOK,
215 1106,
216 "FOK order rejected: unable to fill entire quantity immediately",
217 ),
218 (ErrorType::OrderAlredyExists, 1109, "Order already exists"),
219 (ErrorType::OrderNotFound, 1110, "Order not found"),
220 (ErrorType::OrderBookEmpty, 1200, "Order book is empty"),
221 (ErrorType::InsufficientQuantity, 1201, "Insufficient quantity to calculate price"),
222 (ErrorType::InvalidPriceLevel, 1202, "Invalid order price level"),
223 ];
224
225 for (err_type, code, msg) in cases {
226 assert_eq!(err_type.code(), code);
227 assert_eq!(err_type.message(), msg);
228 }
229 }
230
231 #[test]
232 fn test_error_type_code_and_message() {
233 assert_eq!(ErrorType::Default.code(), 1000);
234 assert_eq!(ErrorType::InvalidPrice.message(), "Invalid order price");
235 assert_eq!(ErrorType::OrderBookEmpty.code(), 1200);
236 assert_eq!(ErrorType::InvalidPriceLevel.message(), "Invalid order price level");
237 }
238
239 #[test]
240 fn test_order_book_error_new() {
241 let err = OrderBookError::new(1234, "Custom error");
242 assert_eq!(err.code, 1234);
243 assert_eq!(err.message, "Custom error");
244 assert_eq!(err.to_string(), "[1234] Custom error");
245 }
246
247 #[test]
248 fn test_default_message_for_code() {
249 assert_eq!(default_message_for_code(1000), ErrorType::Default.message());
250 assert_eq!(default_message_for_code(1101), ErrorType::InvalidQuantity.message());
251 assert_eq!(default_message_for_code(1102), ErrorType::InvalidPrice.message());
252 assert_eq!(default_message_for_code(1103), ErrorType::InvalidPriceOrQuantity.message());
253 assert_eq!(default_message_for_code(1104), ErrorType::OrderPostOnly.message());
254 assert_eq!(default_message_for_code(1105), ErrorType::OrderIOC.message());
255 assert_eq!(default_message_for_code(1106), ErrorType::OrderFOK.message());
256 assert_eq!(default_message_for_code(1109), ErrorType::OrderAlredyExists.message());
257 assert_eq!(default_message_for_code(1110), ErrorType::OrderNotFound.message());
258 assert_eq!(default_message_for_code(1200), ErrorType::InsufficientQuantity.message());
259 assert_eq!(default_message_for_code(1201), ErrorType::InvalidPriceLevel.message());
260 }
261
262 #[test]
263 fn test_order_book_error_from_code_known() {
264 let err = OrderBookError::from_code(1102);
265 assert_eq!(err.code, 1102);
266 assert_eq!(err.message, "Invalid order price");
267 }
268
269 #[test]
270 fn test_order_book_error_from_code_unknown() {
271 let err = OrderBookError::from_code(9999);
272 assert_eq!(err.code, 9999);
273 assert_eq!(err.message, "Unknown error (9999)");
274 }
275
276 #[test]
277 fn test_order_book_error_from_message() {
278 let err = OrderBookError::from_message("Oops");
279 assert_eq!(err.code, 1000);
280 assert_eq!(err.message, "Oops");
281 }
282
283 #[test]
284 fn test_default_message_for_code_known() {
285 assert_eq!(default_message_for_code(1101), "Invalid order quantity");
286 }
287
288 #[test]
289 fn test_default_message_for_code_unknown() {
290 assert_eq!(default_message_for_code(4242), "Unknown error (4242)");
291 }
292
293 #[test]
294 fn test_into_order_book_error_from_error_type() {
295 let err: OrderBookError = ErrorType::OrderAlredyExists.into_error();
296 assert_eq!(err.code, 1109);
297 assert_eq!(err.message, "Order already exists");
298 }
299
300 #[test]
301 fn test_into_order_book_error_from_u32() {
302 let err: OrderBookError = 1110u32.into_error();
303 assert_eq!(err.code, 1110);
304 assert_eq!(err.message, "Order not found");
305 }
306
307 #[test]
308 fn test_into_order_book_error_from_str() {
309 let err: OrderBookError = "Something bad".into_error();
310 assert_eq!(err.code, 1000);
311 assert_eq!(err.message, "Something bad");
312 }
313
314 #[test]
315 fn test_into_order_book_error_from_string() {
316 let err: OrderBookError = String::from("Failure").into_error();
317 assert_eq!(err.code, 1000);
318 assert_eq!(err.message, "Failure");
319 }
320
321 #[test]
322 fn test_make_error_utility() {
323 let err1 = make_error(ErrorType::InvalidQuantity);
324 assert_eq!(err1.code, 1101);
325
326 let err2 = make_error(1103u32);
327 assert_eq!(err2.message, "Invalid order price or quantity");
328
329 let err3 = make_error("Oops");
330 assert_eq!(err3.message, "Oops");
331
332 let err4 = make_error(String::from("Boom"));
333 assert_eq!(err4.message, "Boom");
334 }
335}