1use serde::{Deserialize, Serialize};
2
3pub const MANAGED_REQUEST_VERSION: u32 = 1;
4
5#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
6pub struct ManagedRequestEnvelope {
7 pub v: u32,
8 pub operation_id: u128,
9 #[serde(with = "crate::encoding::bin_bytes")]
10 pub payload: Vec<u8>,
11}
12
13impl ManagedRequestEnvelope {
14 pub fn validate(&self) -> Result<(), &'static str> {
15 if self.v != MANAGED_REQUEST_VERSION {
16 return Err("unsupported managed request version");
17 }
18 if self.operation_id == 0 {
19 return Err("managed request operation id must not be zero");
20 }
21 Ok(())
22 }
23}
24
25#[derive(Clone, Debug, Serialize, Deserialize)]
30pub struct MutationCommandEnvelope {
31 pub v: u32,
32 pub operation_id: u128,
33 pub timestamp_micros: u64,
34 pub command_code: u32,
35 #[serde(with = "crate::encoding::bin_bytes")]
36 pub payload: Vec<u8>,
37}
38
39impl MutationCommandEnvelope {
40 pub fn validate(&self) -> Result<(), &'static str> {
41 if self.v == 0 {
42 return Err("mutation command version must not be zero");
43 }
44 if self.operation_id == 0 {
45 return Err("mutation command operation id must not be zero");
46 }
47 Ok(())
48 }
49}
50
51#[cfg(all(test, feature = "cbor"))]
52mod tests {
53 use super::*;
54 use crate::codes::{AGDX_KV_SET_CODE, KV_OP_VERSION};
55 use crate::framing::{decode_named, encode_named};
56
57 #[test]
58 fn given_a_command_envelope_when_round_tripped_then_should_preserve_request_bytes() {
59 let envelope = MutationCommandEnvelope {
60 v: KV_OP_VERSION,
61 operation_id: 42,
62 timestamp_micros: 1_700_000_000_000_000,
63 command_code: AGDX_KV_SET_CODE,
64 payload: vec![0, 1, 2, 255],
65 };
66 let bytes = encode_named(&envelope).expect("encodes");
67 let back: MutationCommandEnvelope = decode_named(&bytes).expect("decodes");
68 back.validate().expect("the envelope is valid");
69 assert_eq!(back.v, KV_OP_VERSION);
70 assert_eq!(back.operation_id, 42);
71 assert_eq!(back.timestamp_micros, 1_700_000_000_000_000);
72 assert_eq!(back.command_code, AGDX_KV_SET_CODE);
73 assert_eq!(back.payload, vec![0, 1, 2, 255]);
74 }
75
76 #[test]
77 fn given_a_managed_request_when_round_tripped_then_should_preserve_operation_identity() {
78 let request = ManagedRequestEnvelope {
79 v: MANAGED_REQUEST_VERSION,
80 operation_id: u128::MAX,
81 payload: vec![4, 3, 2, 1],
82 };
83
84 let bytes = encode_named(&request).expect("managed request encodes");
85 let back: ManagedRequestEnvelope = decode_named(&bytes).expect("managed request decodes");
86
87 assert_eq!(back, request);
88 back.validate().expect("the request is valid");
89 }
90
91 #[test]
92 fn given_a_zero_operation_identity_when_validated_then_should_reject_it() {
93 let request = ManagedRequestEnvelope {
94 v: MANAGED_REQUEST_VERSION,
95 operation_id: 0,
96 payload: Vec::new(),
97 };
98 let command = MutationCommandEnvelope {
99 v: KV_OP_VERSION,
100 operation_id: 0,
101 timestamp_micros: 1,
102 command_code: AGDX_KV_SET_CODE,
103 payload: Vec::new(),
104 };
105
106 assert!(request.validate().is_err());
107 assert!(command.validate().is_err());
108 }
109}