miltr_common/commands/
recipient.rs1use std::borrow::Cow;
2
3use bytes::{BufMut, BytesMut};
4
5use crate::decoding::Parsable;
6use crate::encoding::Writable;
7use crate::{InvalidData, ProtocolError};
8use miltr_utils::ByteParsing;
9
10#[derive(Clone, PartialEq, Debug, Default)]
12pub struct Recipient {
13 recipient: BytesMut,
14 esmtp_args: Option<BytesMut>,
15}
16
17impl From<&[u8]> for Recipient {
18 fn from(value: &[u8]) -> Self {
19 Self {
20 recipient: BytesMut::from_iter(value),
21 esmtp_args: None,
22 }
23 }
24}
25
26impl Recipient {
27 const CODE: u8 = b'R';
28 #[must_use]
30 pub fn recipient(&self) -> Cow<str> {
31 String::from_utf8_lossy(&self.recipient)
32 }
33
34 pub fn esmtp_args(&self) -> Vec<Cow<str>> {
38 let Some(args) = &self.esmtp_args else {
39 return Vec::new();
40 };
41
42 args[..]
43 .split(|&b| b == 0)
44 .map(String::from_utf8_lossy)
45 .collect()
46 }
47}
48
49impl Parsable for Recipient {
50 const CODE: u8 = Self::CODE;
51
52 fn parse(mut buffer: BytesMut) -> Result<Self, ProtocolError> {
53 let Some(recipient) = buffer.delimited(0) else {
54 return Err(InvalidData::new(
55 "Received recipient package without recipient terminated by null byte in it",
56 buffer,
57 )
58 .into());
59 };
60
61 let esmtp_args = {
62 if buffer.is_empty() {
63 None
64 } else {
65 Some(buffer)
66 }
67 };
68
69 Ok(Self {
70 recipient,
71 esmtp_args,
72 })
73 }
74}
75
76impl Writable for Recipient {
77 fn write(&self, buffer: &mut BytesMut) {
78 buffer.extend_from_slice(&self.recipient);
79 buffer.put_u8(0);
80
81 if let Some(b) = &self.esmtp_args {
82 buffer.extend_from_slice(b);
83 }
84 }
85
86 fn len(&self) -> usize {
87 self.recipient.len()
88 + 1
89 + self
90 .esmtp_args
91 .as_ref()
92 .map(BytesMut::len)
93 .unwrap_or_default()
94 }
95
96 fn code(&self) -> u8 {
97 Self::CODE
98 }
99
100 fn is_empty(&self) -> bool {
101 self.recipient.is_empty() && self.esmtp_args.is_some()
102 }
103}
104
105#[cfg(test)]
106mod test {
107 use super::*;
108 use crate::decoding::Parsable;
109 use rstest::rstest;
110
111 #[rstest]
112 #[case(BytesMut::from("recipient1 recipient2\0arg1\0arg2"), Ok( Recipient {recipient: BytesMut::from("recipient1 recipient2"), esmtp_args: Some(BytesMut::from("arg1\0arg2"))}))]
113 #[case(
114 BytesMut::from("recipient1 arg1 arg2"),
115 Err(InvalidData::new(
116 "Received recipient package without recipient terminated by null byte in it",
117 BytesMut::new(),
118 ))
119 )]
120 fn test_recipient(#[case] input: BytesMut, #[case] expected: Result<Recipient, InvalidData>) {
121 let parsed_recp = Recipient::parse(input);
122
123 match parsed_recp {
124 Ok(recp) => {
125 let expected_recp = expected.unwrap();
126 assert_eq!(recp.recipient, expected_recp.recipient);
127 assert_eq!(recp.esmtp_args, expected_recp.esmtp_args);
128
129 let vec: Vec<Cow<'_, str>> = vec![
131 String::from_utf8_lossy(b"arg1"),
132 String::from_utf8_lossy(b"arg2"),
133 ];
134 assert_eq!(recp.esmtp_args(), vec);
135 }
136 Err(ProtocolError::InvalidData(e)) => {
137 assert_eq!(e.msg, expected.unwrap_err().msg);
138 }
139 _ => panic!("Wrong error received"),
140 }
141 }
142
143 #[cfg(feature = "count-allocations")]
144 #[test]
145 fn test_parse_recipient() {
146 use super::Recipient;
147
148 let buffer = BytesMut::from("rcpt\0arg1\0arg2");
149 let info = allocation_counter::measure(|| {
150 let res = Recipient::parse(buffer);
151 allocation_counter::opt_out(|| {
152 println!("{res:?}");
153 assert!(res.is_ok());
154 });
155 });
156 assert!((0..2).contains(&info.count_total));
158 }
159}