orderbook_rs/orderbook/
serialization.rs1use crate::orderbook::book_change_event::PriceLevelChangedEvent;
24use crate::orderbook::trade::TradeResult;
25
26#[derive(Debug)]
28pub struct SerializationError {
29 pub message: String,
31}
32
33impl std::fmt::Display for SerializationError {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 write!(f, "event serialization error: {}", self.message)
36 }
37}
38
39impl std::error::Error for SerializationError {}
40
41pub trait EventSerializer: Send + Sync + std::fmt::Debug {
53 fn serialize_trade(&self, trade: &TradeResult) -> Result<Vec<u8>, SerializationError>;
59
60 fn serialize_book_change(
66 &self,
67 event: &PriceLevelChangedEvent,
68 ) -> Result<Vec<u8>, SerializationError>;
69
70 fn deserialize_trade(&self, data: &[u8]) -> Result<TradeResult, SerializationError>;
77
78 fn deserialize_book_change(
85 &self,
86 data: &[u8],
87 ) -> Result<PriceLevelChangedEvent, SerializationError>;
88
89 #[must_use]
94 fn content_type(&self) -> &'static str;
95}
96
97#[derive(Debug, Clone, Copy, Default)]
109pub struct JsonEventSerializer;
110
111impl JsonEventSerializer {
112 #[must_use]
114 #[inline]
115 pub fn new() -> Self {
116 Self
117 }
118}
119
120impl EventSerializer for JsonEventSerializer {
121 fn serialize_trade(&self, trade: &TradeResult) -> Result<Vec<u8>, SerializationError> {
122 serde_json::to_vec(trade).map_err(|e| SerializationError {
123 message: e.to_string(),
124 })
125 }
126
127 fn serialize_book_change(
128 &self,
129 event: &PriceLevelChangedEvent,
130 ) -> Result<Vec<u8>, SerializationError> {
131 serde_json::to_vec(event).map_err(|e| SerializationError {
132 message: e.to_string(),
133 })
134 }
135
136 fn deserialize_trade(&self, data: &[u8]) -> Result<TradeResult, SerializationError> {
137 serde_json::from_slice(data).map_err(|e| SerializationError {
138 message: e.to_string(),
139 })
140 }
141
142 fn deserialize_book_change(
143 &self,
144 data: &[u8],
145 ) -> Result<PriceLevelChangedEvent, SerializationError> {
146 serde_json::from_slice(data).map_err(|e| SerializationError {
147 message: e.to_string(),
148 })
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::serialize(trade).map_err(|e| SerializationError {
195 message: e.to_string(),
196 })
197 }
198
199 fn serialize_book_change(
200 &self,
201 event: &PriceLevelChangedEvent,
202 ) -> Result<Vec<u8>, SerializationError> {
203 bincode::serialize(event).map_err(|e| SerializationError {
204 message: e.to_string(),
205 })
206 }
207
208 fn deserialize_trade(&self, data: &[u8]) -> Result<TradeResult, SerializationError> {
209 bincode::deserialize(data).map_err(|e| SerializationError {
210 message: e.to_string(),
211 })
212 }
213
214 fn deserialize_book_change(
215 &self,
216 data: &[u8],
217 ) -> Result<PriceLevelChangedEvent, SerializationError> {
218 bincode::deserialize(data).map_err(|e| SerializationError {
219 message: e.to_string(),
220 })
221 }
222
223 #[inline]
224 fn content_type(&self) -> &'static str {
225 "application/x-bincode"
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232 use pricelevel::{Id, MatchResult, Side};
233
234 fn make_trade_result() -> TradeResult {
235 let order_id = Id::new_uuid();
236 let match_result = MatchResult::new(order_id, 100);
237 TradeResult::new("BTC/USD".to_string(), match_result)
238 }
239
240 fn make_book_change() -> PriceLevelChangedEvent {
241 PriceLevelChangedEvent {
242 side: Side::Buy,
243 price: 50_000_000,
244 quantity: 1_000,
245 }
246 }
247
248 #[test]
251 fn test_json_serialize_trade() {
252 let serializer = JsonEventSerializer::new();
253 let trade = make_trade_result();
254 let result = serializer.serialize_trade(&trade);
255 assert!(result.is_ok());
256 let bytes = result.unwrap_or_default();
257 assert!(!bytes.is_empty());
258
259 let json_str = String::from_utf8(bytes).unwrap_or_default();
260 assert!(json_str.contains("BTC/USD"));
261 }
262
263 #[test]
264 fn test_json_roundtrip_trade() {
265 let serializer = JsonEventSerializer::new();
266 let trade = make_trade_result();
267 let bytes = serializer.serialize_trade(&trade);
268 assert!(bytes.is_ok());
269 let bytes = bytes.unwrap_or_default();
270
271 let decoded = serializer.deserialize_trade(&bytes);
272 assert!(decoded.is_ok());
273 let decoded = decoded.unwrap_or_else(|_| make_trade_result());
274 assert_eq!(decoded.symbol, trade.symbol);
275 assert_eq!(decoded.total_maker_fees, trade.total_maker_fees);
276 assert_eq!(decoded.total_taker_fees, trade.total_taker_fees);
277 }
278
279 #[test]
280 fn test_json_serialize_book_change() {
281 let serializer = JsonEventSerializer::new();
282 let event = make_book_change();
283 let result = serializer.serialize_book_change(&event);
284 assert!(result.is_ok());
285 let bytes = result.unwrap_or_default();
286 assert!(!bytes.is_empty());
287 }
288
289 #[test]
290 fn test_json_roundtrip_book_change() {
291 let serializer = JsonEventSerializer::new();
292 let event = make_book_change();
293 let bytes = serializer.serialize_book_change(&event);
294 assert!(bytes.is_ok());
295 let bytes = bytes.unwrap_or_default();
296
297 let decoded = serializer.deserialize_book_change(&bytes);
298 assert!(decoded.is_ok());
299 let decoded = decoded.unwrap_or_else(|_| make_book_change());
300 assert_eq!(decoded, event);
301 }
302
303 #[test]
304 fn test_json_content_type() {
305 let serializer = JsonEventSerializer::new();
306 assert_eq!(serializer.content_type(), "application/json");
307 }
308
309 #[test]
310 fn test_json_deserialize_trade_error() {
311 let serializer = JsonEventSerializer::new();
312 let result = serializer.deserialize_trade(b"not valid json");
313 assert!(result.is_err());
314 }
315
316 #[test]
317 fn test_json_deserialize_book_change_error() {
318 let serializer = JsonEventSerializer::new();
319 let result = serializer.deserialize_book_change(b"not valid json");
320 assert!(result.is_err());
321 }
322
323 #[test]
324 fn test_serialization_error_display() {
325 let err = SerializationError {
326 message: "test error".to_string(),
327 };
328 let display = format!("{err}");
329 assert!(display.contains("event serialization error"));
330 assert!(display.contains("test error"));
331 }
332
333 #[cfg(feature = "bincode")]
336 mod bincode_tests {
337 use super::*;
338
339 #[test]
340 fn test_bincode_serialize_trade() {
341 let serializer = BincodeEventSerializer::new();
342 let trade = make_trade_result();
343 let result = serializer.serialize_trade(&trade);
344 assert!(result.is_ok());
345 let bytes = result.unwrap_or_default();
346 assert!(!bytes.is_empty());
347
348 let json_serializer = JsonEventSerializer::new();
350 let json_bytes = json_serializer.serialize_trade(&trade).unwrap_or_default();
351 assert!(
352 bytes.len() < json_bytes.len(),
353 "bincode ({}) should be smaller than json ({})",
354 bytes.len(),
355 json_bytes.len()
356 );
357 }
358
359 #[test]
360 fn test_bincode_roundtrip_trade() {
361 let serializer = BincodeEventSerializer::new();
362 let trade = make_trade_result();
363 let bytes = serializer.serialize_trade(&trade);
364 assert!(bytes.is_ok());
365 let bytes = bytes.unwrap_or_default();
366
367 let decoded = serializer.deserialize_trade(&bytes);
368 assert!(decoded.is_ok());
369 let decoded = decoded.unwrap_or_else(|_| make_trade_result());
370 assert_eq!(decoded.symbol, trade.symbol);
371 assert_eq!(decoded.total_maker_fees, trade.total_maker_fees);
372 assert_eq!(decoded.total_taker_fees, trade.total_taker_fees);
373 }
374
375 #[test]
376 fn test_bincode_serialize_book_change() {
377 let serializer = BincodeEventSerializer::new();
378 let event = make_book_change();
379 let result = serializer.serialize_book_change(&event);
380 assert!(result.is_ok());
381 let bytes = result.unwrap_or_default();
382 assert!(!bytes.is_empty());
383 }
384
385 #[test]
386 fn test_bincode_roundtrip_book_change() {
387 let serializer = BincodeEventSerializer::new();
388 let event = make_book_change();
389 let bytes = serializer.serialize_book_change(&event);
390 assert!(bytes.is_ok());
391 let bytes = bytes.unwrap_or_default();
392
393 let decoded = serializer.deserialize_book_change(&bytes);
394 assert!(decoded.is_ok());
395 let decoded = decoded.unwrap_or_else(|_| make_book_change());
396 assert_eq!(decoded, event);
397 }
398
399 #[test]
400 fn test_bincode_content_type() {
401 let serializer = BincodeEventSerializer::new();
402 assert_eq!(serializer.content_type(), "application/x-bincode");
403 }
404
405 #[test]
406 fn test_bincode_deserialize_trade_error() {
407 let serializer = BincodeEventSerializer::new();
408 let result = serializer.deserialize_trade(b"\x00\x01");
409 assert!(result.is_err());
410 }
411
412 #[test]
413 fn test_bincode_deserialize_book_change_error() {
414 let serializer = BincodeEventSerializer::new();
415 let result = serializer.deserialize_book_change(b"\x00\x01");
416 assert!(result.is_err());
417 }
418
419 #[test]
420 fn test_bincode_smaller_than_json_book_change() {
421 let event = make_book_change();
422 let bincode_ser = BincodeEventSerializer::new();
423 let json_ser = JsonEventSerializer::new();
424
425 let bin_bytes = bincode_ser
426 .serialize_book_change(&event)
427 .unwrap_or_default();
428 let json_bytes = json_ser.serialize_book_change(&event).unwrap_or_default();
429
430 assert!(
431 bin_bytes.len() < json_bytes.len(),
432 "bincode ({}) should be smaller than json ({})",
433 bin_bytes.len(),
434 json_bytes.len()
435 );
436 }
437 }
438}