ouch_model 5.0.1

Data model bindings for ouch exchange protocol
Documentation
use crate::prelude::*;
use byteserde::prelude::*;
use byteserde_derive::{ByteDeserializeSlice, ByteSerializeStack, ByteSerializedLenOf};
use serde::{Deserialize, Serialize};

#[derive(ByteSerializeStack, ByteDeserializeSlice, ByteSerializedLenOf, Serialize, Deserialize, PartialEq, Clone, Debug)]
#[byteserde(endian = "be")]
#[serde(from = "OrderExecutedJsonDes")]
pub struct OrderExecuted {
    #[serde(default, skip_serializing)]
    packet_type: PacketTypeOrderExecuted,
    pub timestamp: Timestamp, // Venue assigned
    pub user_ref_number: UserRefNumber,
    pub quantity: Quantity,
    pub price: Price,
    pub liquidity_flag: LiquidityFlag,
    pub match_number: MatchNumber,
    #[serde(skip)]
    #[byteserde(replace( appendages.byte_len() ))]
    appendage_length: u16,
    #[byteserde(deplete(appendage_length))]
    pub appendages: EnterOrderAppendage,
}

impl From<&EnterOrder> for OrderExecuted {
    fn from(enter_order: &EnterOrder) -> Self {
        Self {
            packet_type: PacketTypeOrderExecuted::default(),
            timestamp: Timestamp::default(), // Venue assigned
            user_ref_number: enter_order.user_ref_number,
            quantity: enter_order.quantity,
            price: enter_order.price,
            liquidity_flag: LiquidityFlag::added(),
            match_number: MatchNumber::default(),
            appendage_length: enter_order.appendages.byte_len() as u16,
            appendages: enter_order.appendages,
        }
    }
}

#[derive(Deserialize)]
struct OrderExecutedJsonDes {
    timestamp: Timestamp, // Venue assigned
    user_ref_number: UserRefNumber,
    quantity: Quantity,
    price: Price,
    liquidity_flag: LiquidityFlag,
    match_number: MatchNumber,
    appendages: EnterOrderAppendage,
}
impl From<OrderExecutedJsonDes> for OrderExecuted {
    fn from(value: OrderExecutedJsonDes) -> Self {
        Self {
            packet_type: PacketTypeOrderExecuted::default(),
            timestamp: value.timestamp, // Venue assigned
            user_ref_number: value.user_ref_number,
            quantity: value.quantity,
            price: value.price,
            liquidity_flag: value.liquidity_flag,
            match_number: value.match_number,
            appendage_length: value.appendages.byte_len() as u16,
            appendages: value.appendages,
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use links_core::unittest::setup;

    use log::info;
    use serde_json::to_string;
    use text_diff::{diff, print_diff};

    #[test]
    fn test_msg_byteserde() {
        setup::log::configure();
        let enter_order = EnterOrder::default();
        let msg_inp = OrderExecuted::from(&enter_order);

        let ser: ByteSerializerStack<128> = to_serializer_stack(&msg_inp).unwrap();
        info!("ser: {:#x}", ser);

        let msg_out: OrderExecuted = from_serializer_stack(&ser).unwrap();

        info!("msg_inp: {:?}", msg_inp);
        info!("msg_out: {:?}", msg_out);
        assert_eq!(msg_out, msg_inp);
    }

    #[test]
    fn test_msg_serde() {
        setup::log::configure();
        let enter_order = EnterOrder::default();
        let mut msg_inp = OrderExecuted::from(&enter_order);
        msg_inp.timestamp = 1.into();
        // info!("msg_inp: {:?}", msg_inp);

        let json_out = to_string(&msg_inp).unwrap();
        let json_exp = r#"{"timestamp":1,"user_ref_number":1,"quantity":100,"price":1.2345,"liquidity_flag":"ADDED","match_number":0,"appendages":{"firm":"????","min_qty":0,"customer_type":"PORT_DEFAULT","max_floor":0,"price_type":"LIMIT","peg_offset":-1.1234,"discretion_price":0.0,"discretion_price_type":"LIMIT","discretion_peg_offset":-1.1234,"post_only":"NO","random_reserves":0,"route":"????","expire_time":0,"trade_now":"PORT_DEFAULT","handle_inst":"NO_INSTRUCTIONS","group_id":0,"shares_located":"NO"}}"#;
        info!("json_out: {}", json_out);

        if matches!(diff(&json_out, json_exp, ","), (dist, _) if dist != 0) {
            print_diff(&json_out, json_exp, ",");
            assert_eq!(json_out, json_exp);
        }

        let msg_out: OrderExecuted = serde_json::from_str(&json_out).unwrap();
        // info!("msg_out: {:?}", msg_out);
        assert_eq!(msg_out, msg_inp);
    }
}