orderbook_rs/orderbook/
serialization.rs1use crate::orderbook::book_change_event::PriceLevelChangedEvent;
24use crate::orderbook::trade::TradeResult;
25
26#[derive(Debug, thiserror::Error)]
34pub enum SerializationError {
35 #[error("JSON serialization error: {0}")]
37 Json(#[from] serde_json::Error),
38
39 #[error("bincode serialization error: {0}")]
41 Bincode(String),
42
43 #[error("{0}")]
46 TrailingBytes(String),
47}
48
49pub trait EventSerializer: Send + Sync + std::fmt::Debug {
61 fn serialize_trade(&self, trade: &TradeResult) -> Result<Vec<u8>, SerializationError>;
67
68 fn serialize_book_change(
74 &self,
75 event: &PriceLevelChangedEvent,
76 ) -> Result<Vec<u8>, SerializationError>;
77
78 fn deserialize_trade(&self, data: &[u8]) -> Result<TradeResult, SerializationError>;
85
86 fn deserialize_book_change(
93 &self,
94 data: &[u8],
95 ) -> Result<PriceLevelChangedEvent, SerializationError>;
96
97 #[must_use]
102 fn content_type(&self) -> &'static str;
103}
104
105#[derive(Debug, Clone, Copy, Default)]
117pub struct JsonEventSerializer;
118
119impl JsonEventSerializer {
120 #[must_use]
122 #[inline]
123 pub fn new() -> Self {
124 Self
125 }
126}
127
128impl EventSerializer for JsonEventSerializer {
129 fn serialize_trade(&self, trade: &TradeResult) -> Result<Vec<u8>, SerializationError> {
130 serde_json::to_vec(trade).map_err(SerializationError::Json)
131 }
132
133 fn serialize_book_change(
134 &self,
135 event: &PriceLevelChangedEvent,
136 ) -> Result<Vec<u8>, SerializationError> {
137 serde_json::to_vec(event).map_err(SerializationError::Json)
138 }
139
140 fn deserialize_trade(&self, data: &[u8]) -> Result<TradeResult, SerializationError> {
141 serde_json::from_slice(data).map_err(SerializationError::Json)
142 }
143
144 fn deserialize_book_change(
145 &self,
146 data: &[u8],
147 ) -> Result<PriceLevelChangedEvent, SerializationError> {
148 serde_json::from_slice(data).map_err(SerializationError::Json)
149 }
150
151 #[inline]
152 fn content_type(&self) -> &'static str {
153 "application/json"
154 }
155}
156
157#[cfg(feature = "bincode")]
178#[derive(Debug, Clone, Copy, Default)]
179pub struct BincodeEventSerializer;
180
181#[cfg(feature = "bincode")]
182impl BincodeEventSerializer {
183 #[must_use]
185 #[inline]
186 pub fn new() -> Self {
187 Self
188 }
189}
190
191#[cfg(feature = "bincode")]
192impl EventSerializer for BincodeEventSerializer {
193 fn serialize_trade(&self, trade: &TradeResult) -> Result<Vec<u8>, SerializationError> {
194 bincode::serde::encode_to_vec(trade, bincode::config::standard())
195 .map_err(|e| SerializationError::Bincode(e.to_string()))
196 }
197
198 fn serialize_book_change(
199 &self,
200 event: &PriceLevelChangedEvent,
201 ) -> Result<Vec<u8>, SerializationError> {
202 bincode::serde::encode_to_vec(event, bincode::config::standard())
203 .map_err(|e| SerializationError::Bincode(e.to_string()))
204 }
205
206 fn deserialize_trade(&self, data: &[u8]) -> Result<TradeResult, SerializationError> {
207 let (value, bytes_read) =
208 bincode::serde::decode_from_slice::<TradeResult, _>(data, bincode::config::standard())
209 .map_err(|e| SerializationError::Bincode(e.to_string()))?;
210 if bytes_read != data.len() {
211 return Err(SerializationError::TrailingBytes(format!(
212 "trailing bytes after trade payload: consumed {bytes_read} of {}",
213 data.len()
214 )));
215 }
216 Ok(value)
217 }
218
219 fn deserialize_book_change(
220 &self,
221 data: &[u8],
222 ) -> Result<PriceLevelChangedEvent, SerializationError> {
223 let (value, bytes_read) = bincode::serde::decode_from_slice::<PriceLevelChangedEvent, _>(
224 data,
225 bincode::config::standard(),
226 )
227 .map_err(|e| SerializationError::Bincode(e.to_string()))?;
228 if bytes_read != data.len() {
229 return Err(SerializationError::TrailingBytes(format!(
230 "trailing bytes after book-change payload: consumed {bytes_read} of {}",
231 data.len()
232 )));
233 }
234 Ok(value)
235 }
236
237 #[inline]
238 fn content_type(&self) -> &'static str {
239 "application/x-bincode"
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246 use pricelevel::{Id, MatchResult, Quantity, Side};
247
248 fn make_trade_result() -> TradeResult {
249 let order_id = Id::new_uuid();
250 let match_result = MatchResult::new(order_id, Quantity::new(100));
251 TradeResult::new("BTC/USD".to_string(), match_result)
252 }
253
254 fn make_book_change() -> PriceLevelChangedEvent {
255 PriceLevelChangedEvent {
256 side: Side::Buy,
257 price: 50_000_000,
258 quantity: 1_000,
259 engine_seq: 0,
260 }
261 }
262
263 #[test]
266 fn test_json_serialize_trade() {
267 let serializer = JsonEventSerializer::new();
268 let trade = make_trade_result();
269 let result = serializer.serialize_trade(&trade);
270 assert!(result.is_ok());
271 let bytes = result.unwrap_or_default();
272 assert!(!bytes.is_empty());
273
274 let json_str = String::from_utf8(bytes).unwrap_or_default();
275 assert!(json_str.contains("BTC/USD"));
276 }
277
278 #[test]
279 fn test_json_roundtrip_trade() {
280 let serializer = JsonEventSerializer::new();
281 let trade = make_trade_result();
282 let bytes = serializer.serialize_trade(&trade);
283 assert!(bytes.is_ok());
284 let bytes = bytes.unwrap_or_default();
285
286 let decoded = serializer.deserialize_trade(&bytes);
287 assert!(decoded.is_ok());
288 let decoded = decoded.unwrap_or_else(|_| make_trade_result());
289 assert_eq!(decoded.symbol, trade.symbol);
290 assert_eq!(decoded.total_maker_fees, trade.total_maker_fees);
291 assert_eq!(decoded.total_taker_fees, trade.total_taker_fees);
292 }
293
294 #[test]
295 fn test_json_serialize_book_change() {
296 let serializer = JsonEventSerializer::new();
297 let event = make_book_change();
298 let result = serializer.serialize_book_change(&event);
299 assert!(result.is_ok());
300 let bytes = result.unwrap_or_default();
301 assert!(!bytes.is_empty());
302 }
303
304 #[test]
305 fn test_json_roundtrip_book_change() {
306 let serializer = JsonEventSerializer::new();
307 let event = make_book_change();
308 let bytes = serializer.serialize_book_change(&event);
309 assert!(bytes.is_ok());
310 let bytes = bytes.unwrap_or_default();
311
312 let decoded = serializer.deserialize_book_change(&bytes);
313 assert!(decoded.is_ok());
314 let decoded = decoded.unwrap_or_else(|_| make_book_change());
315 assert_eq!(decoded, event);
316 }
317
318 #[test]
319 fn test_json_content_type() {
320 let serializer = JsonEventSerializer::new();
321 assert_eq!(serializer.content_type(), "application/json");
322 }
323
324 #[test]
325 fn test_json_deserialize_trade_error() {
326 let serializer = JsonEventSerializer::new();
327 let result = serializer.deserialize_trade(b"not valid json");
328 assert!(result.is_err());
329 }
330
331 #[test]
332 fn test_json_deserialize_book_change_error() {
333 let serializer = JsonEventSerializer::new();
334 let result = serializer.deserialize_book_change(b"not valid json");
335 assert!(result.is_err());
336 }
337
338 #[test]
339 fn test_serialization_error_display_preserves_underlying() {
340 let serde_err = serde_json::from_str::<i32>("not a number").unwrap_err();
342 let err = SerializationError::Json(serde_err);
343 let display = format!("{err}");
344 assert!(display.contains("JSON serialization error"));
345
346 let trailing = SerializationError::TrailingBytes("consumed 3 of 5".to_string());
347 assert!(format!("{trailing}").contains("consumed 3 of 5"));
348 }
349
350 #[test]
351 fn test_serialization_error_propagates_into_orderbook_error() {
352 use crate::orderbook::error::OrderBookError;
353
354 fn run() -> Result<i32, OrderBookError> {
357 let parsed: i32 = serde_json::from_str("nope").map_err(SerializationError::Json)?;
358 Ok(parsed)
359 }
360
361 match run() {
362 Err(OrderBookError::SerializationError { .. }) => {}
363 other => panic!("expected OrderBookError::SerializationError, got {other:?}"),
364 }
365 }
366
367 #[cfg(feature = "bincode")]
370 mod bincode_tests {
371 use super::*;
372
373 #[test]
374 fn test_bincode_serialize_trade() {
375 let serializer = BincodeEventSerializer::new();
376 let trade = make_trade_result();
377 let result = serializer.serialize_trade(&trade);
378 assert!(result.is_ok());
379 let bytes = result.unwrap_or_default();
380 assert!(!bytes.is_empty());
381
382 let json_serializer = JsonEventSerializer::new();
384 let json_bytes = json_serializer.serialize_trade(&trade).unwrap_or_default();
385 assert!(
386 bytes.len() < json_bytes.len(),
387 "bincode ({}) should be smaller than json ({})",
388 bytes.len(),
389 json_bytes.len()
390 );
391 }
392
393 #[test]
394 fn test_bincode_roundtrip_trade() {
395 let serializer = BincodeEventSerializer::new();
396 let trade = make_trade_result();
397 let bytes = serializer.serialize_trade(&trade);
398 assert!(bytes.is_ok());
399 let bytes = bytes.unwrap_or_default();
400
401 let decoded = serializer.deserialize_trade(&bytes);
402 assert!(decoded.is_ok());
403 let decoded = decoded.unwrap_or_else(|_| make_trade_result());
404 assert_eq!(decoded.symbol, trade.symbol);
405 assert_eq!(decoded.total_maker_fees, trade.total_maker_fees);
406 assert_eq!(decoded.total_taker_fees, trade.total_taker_fees);
407 }
408
409 #[test]
410 fn test_bincode_serialize_book_change() {
411 let serializer = BincodeEventSerializer::new();
412 let event = make_book_change();
413 let result = serializer.serialize_book_change(&event);
414 assert!(result.is_ok());
415 let bytes = result.unwrap_or_default();
416 assert!(!bytes.is_empty());
417 }
418
419 #[test]
420 fn test_bincode_roundtrip_book_change() {
421 let serializer = BincodeEventSerializer::new();
422 let event = make_book_change();
423 let bytes = serializer.serialize_book_change(&event);
424 assert!(bytes.is_ok());
425 let bytes = bytes.unwrap_or_default();
426
427 let decoded = serializer.deserialize_book_change(&bytes);
428 assert!(decoded.is_ok());
429 let decoded = decoded.unwrap_or_else(|_| make_book_change());
430 assert_eq!(decoded, event);
431 }
432
433 #[test]
434 fn test_bincode_content_type() {
435 let serializer = BincodeEventSerializer::new();
436 assert_eq!(serializer.content_type(), "application/x-bincode");
437 }
438
439 #[test]
440 fn test_bincode_deserialize_trade_error() {
441 let serializer = BincodeEventSerializer::new();
442 let result = serializer.deserialize_trade(b"\x00\x01");
443 assert!(result.is_err());
444 }
445
446 #[test]
447 fn test_bincode_deserialize_book_change_error() {
448 let serializer = BincodeEventSerializer::new();
449 let result = serializer.deserialize_book_change(b"\x00\x01");
450 assert!(result.is_err());
451 }
452
453 #[test]
454 fn test_bincode_smaller_than_json_book_change() {
455 let event = make_book_change();
456 let bincode_ser = BincodeEventSerializer::new();
457 let json_ser = JsonEventSerializer::new();
458
459 let bin_bytes = bincode_ser
460 .serialize_book_change(&event)
461 .unwrap_or_default();
462 let json_bytes = json_ser.serialize_book_change(&event).unwrap_or_default();
463
464 assert!(
465 bin_bytes.len() < json_bytes.len(),
466 "bincode ({}) should be smaller than json ({})",
467 bin_bytes.len(),
468 json_bytes.len()
469 );
470 }
471 }
472}