miltr_common/commands/
header.rs1use std::borrow::Cow;
2
3use bytes::{BufMut, BytesMut};
4
5use crate::decoding::Parsable;
6use crate::encoding::Writable;
7use crate::InvalidData;
8use crate::ProtocolError;
9use miltr_utils::ByteParsing;
10
11#[derive(Clone, PartialEq, Debug, Default)]
13pub struct Header {
14 name: BytesMut,
15 value: BytesMut,
16}
17
18impl Header {
19 const CODE: u8 = b'L';
20
21 #[must_use]
23 pub fn new(name: &[u8], value: &[u8]) -> Self {
24 Self {
25 name: BytesMut::from_iter(name),
26 value: BytesMut::from_iter(value),
27 }
28 }
29 #[must_use]
31 pub fn name(&self) -> Cow<str> {
32 String::from_utf8_lossy(&self.name)
33 }
34
35 #[must_use]
37 pub fn value(&self) -> Cow<str> {
38 String::from_utf8_lossy(&self.value)
39 }
40}
41
42impl Parsable for Header {
43 const CODE: u8 = Self::CODE;
44
45 fn parse(mut buffer: BytesMut) -> Result<Self, ProtocolError> {
46 let Some(name) = buffer.delimited(0) else {
47 return Err(InvalidData::new(
48 "Received header package without name terminated by null byte in it",
49 buffer,
50 )
51 .into());
52 };
53
54 let Some(value) = buffer.delimited(0) else {
55 return Err(InvalidData::new(
56 "Received header package without value terminated by null byte in it",
57 buffer,
58 )
59 .into());
60 };
61
62 Ok(Self { name, value })
63 }
64}
65
66impl Writable for Header {
67 fn write(&self, buffer: &mut BytesMut) {
68 buffer.extend_from_slice(&self.name);
69 buffer.put_u8(0);
70 buffer.extend_from_slice(&self.value);
71 buffer.put_u8(0);
72 }
73
74 fn len(&self) -> usize {
75 self.name.len() + 1 + self.value.len() + 1
76 }
77
78 fn code(&self) -> u8 {
79 Self::CODE
80 }
81
82 fn is_empty(&self) -> bool {
83 self.name.is_empty() && self.value.is_empty()
84 }
85}
86
87#[derive(Clone, PartialEq, Debug, Default)]
89pub struct EndOfHeader;
90
91impl EndOfHeader {
92 const CODE: u8 = b'N';
93}
94
95impl Parsable for EndOfHeader {
96 const CODE: u8 = Self::CODE;
97
98 fn parse(_buffer: BytesMut) -> Result<Self, ProtocolError> {
99 Ok(Self)
100 }
101}
102
103impl Writable for EndOfHeader {
104 fn write(&self, _buffer: &mut BytesMut) {}
105
106 fn len(&self) -> usize {
107 0
108 }
109
110 fn code(&self) -> u8 {
111 Self::CODE
112 }
113
114 fn is_empty(&self) -> bool {
115 false
116 }
117}
118
119#[cfg(test)]
120mod test {
121 use super::*;
122 use crate::decoding::Parsable;
123 use pretty_assertions::assert_eq;
124 use rstest::rstest;
125
126 #[rstest]
127 #[case(BytesMut::from("name\0value\0"), Ok(Header {name: BytesMut::from("name"), value: BytesMut::from("value")} ))]
128 #[case(
129 BytesMut::from("name\0value"),
130 Err(InvalidData::new(
131 "Received header package without value terminated by null byte in it",
132 BytesMut::new()
133 ))
134 )]
135 #[case(
136 BytesMut::from("namevalue\0"),
137 Err(InvalidData::new(
138 "Received header package without value terminated by null byte in it",
139 BytesMut::new()
140 ))
141 )]
142 fn test_header(#[case] input: BytesMut, #[case] expected: Result<Header, InvalidData>) {
143 let parsed_header = Header::parse(input);
144
145 match (expected, parsed_header) {
146 (Err(expected), Err(ProtocolError::InvalidData(parsed))) => {
147 assert_eq!(expected.msg, parsed.msg);
148 }
149 (Ok(expected), Ok(parsed)) => assert_eq!(expected, parsed),
150 (expected, parsed) => panic!("Did not get expected:\n{expected:?}\n vs \n{parsed:?}"),
151 }
152 }
153 #[cfg(feature = "count-allocations")]
154 #[test]
155 fn test_parse_header() {
156 let buffer = BytesMut::from("name\0value\0");
157
158 let info = allocation_counter::measure(|| {
159 let res = Header::parse(buffer);
160
161 allocation_counter::opt_out(|| {
162 println!("{res:?}");
163 assert!(res.is_ok());
164 });
165 });
166
167 println!("{info:#?}");
168 assert_eq!(info.count_total, 1);
169 }
170}