use crate::wire::error::WireError;
pub const TRADE_PRINT_SIZE: usize = 48;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TradePrintWire {
pub engine_seq: u64,
pub maker_id: u64,
pub taker_id: u64,
pub price: i64,
pub qty: u64,
pub ts: u64,
}
#[inline]
pub fn encode_trade_print(trade: &TradePrintWire, out: &mut Vec<u8>) {
out.reserve(TRADE_PRINT_SIZE);
out.extend_from_slice(&trade.engine_seq.to_le_bytes());
out.extend_from_slice(&trade.maker_id.to_le_bytes());
out.extend_from_slice(&trade.taker_id.to_le_bytes());
out.extend_from_slice(&trade.price.to_le_bytes());
out.extend_from_slice(&trade.qty.to_le_bytes());
out.extend_from_slice(&trade.ts.to_le_bytes());
}
#[inline]
pub fn decode_trade_print(payload: &[u8]) -> Result<TradePrintWire, WireError> {
if payload.len() != TRADE_PRINT_SIZE {
return Err(WireError::InvalidPayload(
"TradePrint: payload size mismatch",
));
}
let read_u64 = |offset: usize| -> Result<u64, WireError> {
let slot = payload
.get(offset..offset + 8)
.ok_or(WireError::Truncated)?;
let mut arr = [0u8; 8];
arr.copy_from_slice(slot);
Ok(u64::from_le_bytes(arr))
};
let read_i64 = |offset: usize| -> Result<i64, WireError> {
let slot = payload
.get(offset..offset + 8)
.ok_or(WireError::Truncated)?;
let mut arr = [0u8; 8];
arr.copy_from_slice(slot);
Ok(i64::from_le_bytes(arr))
};
Ok(TradePrintWire {
engine_seq: read_u64(0)?,
maker_id: read_u64(8)?,
taker_id: read_u64(16)?,
price: read_i64(24)?,
qty: read_u64(32)?,
ts: read_u64(40)?,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::wire::framing::{decode_frame, encode_frame};
use proptest::prelude::*;
#[test]
fn payload_size_constant() {
let trade = TradePrintWire {
engine_seq: 0,
maker_id: 0,
taker_id: 0,
price: 0,
qty: 0,
ts: 0,
};
let mut buf = Vec::new();
encode_trade_print(&trade, &mut buf);
assert_eq!(buf.len(), TRADE_PRINT_SIZE);
}
proptest! {
#[test]
fn roundtrip_through_frame(
engine_seq in any::<u64>(),
maker_id in any::<u64>(),
taker_id in any::<u64>(),
price in any::<i64>(),
qty in any::<u64>(),
ts in any::<u64>(),
) {
let original = TradePrintWire {
engine_seq,
maker_id,
taker_id,
price,
qty,
ts,
};
let mut payload = Vec::new();
encode_trade_print(&original, &mut payload);
let mut framed = Vec::new();
encode_frame(0x82, &payload, &mut framed).expect("encode_frame");
let (kind, decoded_payload, _) = decode_frame(&framed).expect("decode_frame");
prop_assert_eq!(kind, 0x82u8);
let decoded = decode_trade_print(decoded_payload).expect("decode_trade_print");
prop_assert_eq!(decoded, original);
}
}
#[test]
fn rejects_wrong_size() {
let buf = [0u8; TRADE_PRINT_SIZE - 1];
assert!(matches!(
decode_trade_print(&buf),
Err(WireError::InvalidPayload(_))
));
}
}