1use byteorder::{BigEndian, ByteOrder};
2use thiserror::Error;
3
4#[derive(Error, Debug)]
5pub enum PapError {
6 #[error("invalid size - expected at least {expected} bytes but found {found}")]
7 InvalidSize { expected: usize, found: usize },
8 #[error("unknown function code {code}")]
9 UnknownFunction { code: u8 },
10}
11
12#[derive(Debug, Copy, Clone, PartialEq, Eq)]
14#[repr(u8)]
15pub enum PapFunction {
16 OpenConn = 1,
17 OpenConnReply = 2,
18 SendData = 3,
19 Data = 4,
20 Tickle = 5,
21 CloseConn = 6,
22 CloseConnReply = 7,
23 SendStatus = 8,
24 Status = 9,
25}
26
27impl TryFrom<u8> for PapFunction {
28 type Error = PapError;
29
30 fn try_from(value: u8) -> Result<Self, Self::Error> {
31 match value {
32 1 => Ok(PapFunction::OpenConn),
33 2 => Ok(PapFunction::OpenConnReply),
34 3 => Ok(PapFunction::SendData),
35 4 => Ok(PapFunction::Data),
36 5 => Ok(PapFunction::Tickle),
37 6 => Ok(PapFunction::CloseConn),
38 7 => Ok(PapFunction::CloseConnReply),
39 8 => Ok(PapFunction::SendStatus),
40 9 => Ok(PapFunction::Status),
41 _ => Err(PapError::UnknownFunction { code: value }),
42 }
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct PapPacket {
55 pub connection_id: u8,
56 pub function: PapFunction,
57 pub sequence_num: u16,
58 pub data: Vec<u8>,
59}
60
61impl PapPacket {
62 pub const MIN_HEADER_LEN: usize = 2;
64
65 pub fn parse(buf: &[u8]) -> Result<Self, PapError> {
67 if buf.len() < Self::MIN_HEADER_LEN {
68 return Err(PapError::InvalidSize {
69 expected: Self::MIN_HEADER_LEN,
70 found: buf.len(),
71 });
72 }
73
74 let connection_id = buf[0];
75 let function = PapFunction::try_from(buf[1])?;
76
77 let (sequence_num, data_start) = match function {
79 PapFunction::SendData | PapFunction::Data => {
80 if buf.len() < 4 {
81 return Err(PapError::InvalidSize {
82 expected: 4,
83 found: buf.len(),
84 });
85 }
86 (BigEndian::read_u16(&buf[2..4]), 4)
87 }
88 _ => (0, 2),
89 };
90
91 let data = buf[data_start..].to_vec();
92
93 Ok(Self {
94 connection_id,
95 function,
96 sequence_num,
97 data,
98 })
99 }
100
101 pub fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, PapError> {
103 let has_seq_num = matches!(self.function, PapFunction::SendData | PapFunction::Data);
104
105 let header_len = if has_seq_num { 4 } else { 2 };
106 let total_len = header_len + self.data.len();
107
108 if buf.len() < total_len {
109 return Err(PapError::InvalidSize {
110 expected: total_len,
111 found: buf.len(),
112 });
113 }
114
115 buf[0] = self.connection_id;
116 buf[1] = self.function as u8;
117
118 if has_seq_num {
119 BigEndian::write_u16(&mut buf[2..4], self.sequence_num);
120 buf[4..total_len].copy_from_slice(&self.data);
121 } else {
122 buf[2..total_len].copy_from_slice(&self.data);
123 }
124
125 Ok(total_len)
126 }
127
128 pub fn len(&self) -> usize {
130 let header_len = match self.function {
131 PapFunction::SendData | PapFunction::Data => 4,
132 _ => 2,
133 };
134 header_len + self.data.len()
135 }
136
137 pub fn is_empty(&self) -> bool {
139 self.data.is_empty()
140 }
141
142 pub fn to_atp_parts(&self) -> ([u8; 4], &[u8]) {
149 let mut user_bytes = [0u8; 4];
150 user_bytes[0] = self.connection_id;
151 user_bytes[1] = self.function as u8;
152
153 if matches!(self.function, PapFunction::SendData | PapFunction::Data) {
154 BigEndian::write_u16(&mut user_bytes[2..4], self.sequence_num);
155 }
156 (user_bytes, &self.data)
159 }
160
161 pub fn parse_from_atp(user_bytes: [u8; 4], data: &[u8]) -> Result<Self, PapError> {
163 let connection_id = user_bytes[0];
164 let function = PapFunction::try_from(user_bytes[1])?;
165
166 let sequence_num = match function {
167 PapFunction::SendData | PapFunction::Data => BigEndian::read_u16(&user_bytes[2..4]),
168 _ => 0,
169 };
170
171 Ok(Self {
172 connection_id,
173 function,
174 sequence_num,
175 data: data.to_vec(),
176 })
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn test_parse_open_conn() {
186 let data: &[u8] = &[0x00, 0x01, 0x42, 0x00, 0x08];
188
189 let packet = PapPacket::parse(data).expect("failed to parse");
190
191 assert_eq!(packet.connection_id, 0);
192 assert_eq!(packet.function, PapFunction::OpenConn);
193 assert_eq!(packet.sequence_num, 0);
194 assert_eq!(packet.data, vec![0x42, 0x00, 0x08]);
195 }
196
197 #[test]
198 fn test_parse_send_data() {
199 let data: &[u8] = &[0x05, 0x03, 0x00, 0x01, b'H', b'e', b'l', b'l', b'o'];
201
202 let packet = PapPacket::parse(data).expect("failed to parse");
203
204 assert_eq!(packet.connection_id, 5);
205 assert_eq!(packet.function, PapFunction::SendData);
206 assert_eq!(packet.sequence_num, 1);
207 assert_eq!(packet.data, b"Hello");
208 }
209
210 #[test]
211 fn test_encode_open_conn_reply() {
212 let packet = PapPacket {
213 connection_id: 7,
214 function: PapFunction::OpenConnReply,
215 sequence_num: 0, data: vec![0x42, 0x00, 0x08, 0x00, 0x01], };
218
219 let mut buf = [0u8; 32];
220 let len = packet.to_bytes(&mut buf).expect("failed to encode");
221
222 assert_eq!(len, 7); assert_eq!(&buf[..len], &[0x07, 0x02, 0x42, 0x00, 0x08, 0x00, 0x01]);
224 }
225
226 #[test]
227 fn test_encode_data() {
228 let packet = PapPacket {
229 connection_id: 3,
230 function: PapFunction::Data,
231 sequence_num: 42,
232 data: b"PostScript data".to_vec(),
233 };
234
235 let mut buf = [0u8; 64];
236 let len = packet.to_bytes(&mut buf).expect("failed to encode");
237
238 assert_eq!(len, 4 + 15); assert_eq!(buf[0], 3); assert_eq!(buf[1], 4); assert_eq!(BigEndian::read_u16(&buf[2..4]), 42); assert_eq!(&buf[4..len], b"PostScript data");
243 }
244
245 #[test]
246 fn test_round_trip_tickle() {
247 let original = PapPacket {
248 connection_id: 10,
249 function: PapFunction::Tickle,
250 sequence_num: 0,
251 data: vec![],
252 };
253
254 let mut buf = [0u8; 32];
255 let len = original.to_bytes(&mut buf).expect("failed to encode");
256 assert_eq!(len, 2); let parsed = PapPacket::parse(&buf[..len]).expect("failed to parse");
259 assert_eq!(original, parsed);
260 }
261
262 #[test]
263 fn test_round_trip_with_data() {
264 let original = PapPacket {
265 connection_id: 15,
266 function: PapFunction::SendData,
267 sequence_num: 100,
268 data: b"Test print job data".to_vec(),
269 };
270
271 let mut buf = [0u8; 64];
272 let len = original.to_bytes(&mut buf).expect("failed to encode");
273
274 let parsed = PapPacket::parse(&buf[..len]).expect("failed to parse");
275 assert_eq!(original, parsed);
276 }
277
278 #[test]
279 fn test_invalid_function_code() {
280 let data: &[u8] = &[0x01, 0xFF]; let result = PapPacket::parse(data);
283 assert!(result.is_err());
284 match result {
285 Err(PapError::UnknownFunction { code: 0xFF }) => {}
286 _ => panic!("Expected UnknownFunction error"),
287 }
288 }
289
290 #[test]
291 fn test_buffer_too_small() {
292 let packet = PapPacket {
293 connection_id: 1,
294 function: PapFunction::Status,
295 sequence_num: 0,
296 data: vec![1, 2, 3, 4, 5],
297 };
298
299 let mut buf = [0u8; 4]; let result = packet.to_bytes(&mut buf);
301 assert!(result.is_err());
302 }
303
304 #[test]
305 fn test_atp_helpers() {
306 let original = PapPacket {
307 connection_id: 10,
308 function: PapFunction::SendData,
309 sequence_num: 12345,
310 data: b"Data Payload".to_vec(),
311 };
312
313 let (user_bytes, data) = original.to_atp_parts();
314 assert_eq!(user_bytes[0], 10);
315 assert_eq!(user_bytes[1], 3); assert_eq!(BigEndian::read_u16(&user_bytes[2..4]), 12345);
317 assert_eq!(data, b"Data Payload");
318
319 let parsed = PapPacket::parse_from_atp(user_bytes, data).expect("failed to parse from atp");
320 assert_eq!(original, parsed);
321 }
322}