Skip to main content

laser_wire/
batch.rs

1use serde::{Deserialize, Serialize};
2
3/// The mixed-operation batch request
4/// ([`AGDX_BATCH_CODE`](crate::codes::AGDX_BATCH_CODE)): up to
5/// [`MAX_BATCH_OPS`](crate::limits::MAX_BATCH_OPS) managed requests in one
6/// round trip, each carrying its own command code and encoded payload. The
7/// batch amortizes the round trip and nothing else: items execute
8/// independently in order, each yields its own result, and a failed item
9/// fails alone (explicitly NOT a transaction). A nested batch (an item whose
10/// code is the batch code) is rejected.
11#[derive(Clone, Debug, Serialize, Deserialize)]
12pub struct BatchRequest {
13    /// Op version, [`BATCH_OP_VERSION`](crate::codes::BATCH_OP_VERSION).
14    pub v: u32,
15    /// The managed requests, executed in order.
16    pub ops: Vec<BatchItem>,
17}
18
19/// One managed request inside a [`BatchRequest`]: the command code it would
20/// have been sent under on its own, and its encoded request bytes verbatim.
21#[derive(Clone, Debug, Serialize, Deserialize)]
22pub struct BatchItem {
23    /// The managed command code (e.g. `AGDX_KV_GET_CODE`).
24    pub code: u32,
25    /// The op's own encoded request frame, exactly what a standalone send
26    /// would carry.
27    #[serde(with = "crate::encoding::bin_bytes")]
28    pub payload: Vec<u8>,
29}
30
31/// The batch reply: each op's own reply bytes, in request order, exactly what
32/// a standalone round trip would have returned (including a typed error
33/// reply for an item that failed, so the caller decodes each slot with the
34/// item's own reply type).
35#[derive(Clone, Debug, Serialize, Deserialize)]
36pub struct BatchReply {
37    /// Per-op reply frames, index-aligned with the request's `ops`.
38    #[serde(with = "crate::encoding::vec_bin_bytes")]
39    pub results: Vec<Vec<u8>>,
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45    use crate::framing::{decode_named, encode_named};
46
47    #[test]
48    fn given_a_batch_when_round_tripped_then_should_preserve_items_and_results() {
49        let request = BatchRequest {
50            v: crate::codes::BATCH_OP_VERSION,
51            ops: vec![
52                BatchItem {
53                    code: crate::codes::AGDX_KV_GET_CODE,
54                    payload: b"\xa1av\x01".to_vec(),
55                },
56                BatchItem {
57                    code: crate::codes::AGDX_QUERY_CODE,
58                    payload: Vec::new(),
59                },
60            ],
61        };
62        let decoded: BatchRequest =
63            decode_named(&encode_named(&request).expect("encodes")).expect("decodes");
64        assert_eq!(decoded.ops.len(), 2);
65        assert_eq!(decoded.ops[0].code, crate::codes::AGDX_KV_GET_CODE);
66        assert_eq!(decoded.ops[0].payload, request.ops[0].payload);
67
68        let reply = BatchReply {
69            results: vec![b"ok".to_vec(), Vec::new()],
70        };
71        let decoded: BatchReply =
72            decode_named(&encode_named(&reply).expect("encodes")).expect("decodes");
73        assert_eq!(decoded.results, reply.results);
74    }
75}