melin_server/
response_encoder.rs1use melin_app::encoder::ResponseEncoder as ResponseEncoderTrait;
10use melin_protocol::codec;
11use melin_protocol::message::ResponseKind;
12use melin_types::types::{ExecutionReport, QueryResponse};
13
14#[derive(Debug, Clone, Copy)]
19pub struct ResponseEncoder;
20
21impl ResponseEncoderTrait for ResponseEncoder {
22 type Report = ExecutionReport;
23 type Query = QueryResponse;
24
25 fn encode_report(
26 &self,
27 report: &ExecutionReport,
28 buf: &mut [u8],
29 ) -> Result<usize, &'static str> {
30 codec::encode_response(&ResponseKind::Report(*report), buf).map_err(|_| "encode error")
31 }
32
33 fn encode_query(&self, query: &QueryResponse, buf: &mut [u8]) -> Result<usize, &'static str> {
34 let kind = match *query {
35 QueryResponse::Stats {
36 active_connections,
37 events_processed,
38 journal_sequence,
39 } => ResponseKind::StatsHeader {
40 active_connections,
41 events_processed,
42 journal_sequence,
43 },
44 QueryResponse::Position {
45 account,
46 balances,
47 count,
48 } => ResponseKind::PositionSnapshot {
49 account,
50 balances,
51 count,
52 },
53 QueryResponse::RequestSeqHwm { hwm } => ResponseKind::RequestSeqHwm { hwm },
54 };
55 codec::encode_response(&kind, buf).map_err(|_| "encode error")
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use std::num::NonZeroU64;
63
64 use melin_types::types::*;
65
66 const SCRATCH: usize = 512;
67
68 fn round_trip(written: &[u8]) -> ResponseKind {
72 codec::decode_response(&written[4..]).expect("decode")
73 }
74
75 fn sample_placed() -> ExecutionReport {
76 ExecutionReport::Placed {
77 order_id: OrderId(1),
78 symbol: Symbol(1),
79 account: AccountId(1),
80 side: Side::Buy,
81 price: Price(NonZeroU64::new(100).unwrap()),
82 quantity: Quantity(NonZeroU64::new(10).unwrap()),
83 }
84 }
85
86 #[test]
87 fn encodes_report() {
88 let mut buf = [0u8; SCRATCH];
89 let n = ResponseEncoder
90 .encode_report(&sample_placed(), &mut buf)
91 .unwrap();
92 assert!(matches!(
93 round_trip(&buf[..n]),
94 ResponseKind::Report(ExecutionReport::Placed { order_id, .. })
95 if order_id == OrderId(1)
96 ));
97 }
98
99 #[test]
100 fn encodes_query_stats() {
101 let q = QueryResponse::Stats {
102 active_connections: 7,
103 events_processed: 12345,
104 journal_sequence: 999,
105 };
106 let mut buf = [0u8; SCRATCH];
107 let n = ResponseEncoder.encode_query(&q, &mut buf).unwrap();
108 assert!(matches!(
109 round_trip(&buf[..n]),
110 ResponseKind::StatsHeader {
111 active_connections: 7,
112 events_processed: 12345,
113 journal_sequence: 999,
114 }
115 ));
116 }
117
118 #[test]
119 fn encodes_query_position() {
120 let mut balances = [AccountBalance::ZERO; 16];
121 balances[0] = AccountBalance {
122 currency: CurrencyId(1),
123 free: 100,
124 reserved: 0,
125 };
126 let q = QueryResponse::Position {
127 account: AccountId(42),
128 balances,
129 count: 1,
130 };
131 let mut buf = [0u8; SCRATCH];
132 let n = ResponseEncoder.encode_query(&q, &mut buf).unwrap();
133 assert!(matches!(
134 round_trip(&buf[..n]),
135 ResponseKind::PositionSnapshot { account, count: 1, .. }
136 if account == AccountId(42)
137 ));
138 }
139
140 #[test]
141 fn encodes_query_request_seq_hwm() {
142 let q = QueryResponse::RequestSeqHwm { hwm: 4242 };
143 let mut buf = [0u8; SCRATCH];
144 let n = ResponseEncoder.encode_query(&q, &mut buf).unwrap();
145 assert!(matches!(
146 round_trip(&buf[..n]),
147 ResponseKind::RequestSeqHwm { hwm: 4242 }
148 ));
149 }
150
151 }