use byteorder::{BigEndian, ByteOrder};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum PapError {
#[error("invalid size - expected at least {expected} bytes but found {found}")]
InvalidSize { expected: usize, found: usize },
#[error("unknown function code {code}")]
UnknownFunction { code: u8 },
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum PapFunction {
OpenConn = 1,
OpenConnReply = 2,
SendData = 3,
Data = 4,
Tickle = 5,
CloseConn = 6,
CloseConnReply = 7,
SendStatus = 8,
Status = 9,
}
impl TryFrom<u8> for PapFunction {
type Error = PapError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
1 => Ok(PapFunction::OpenConn),
2 => Ok(PapFunction::OpenConnReply),
3 => Ok(PapFunction::SendData),
4 => Ok(PapFunction::Data),
5 => Ok(PapFunction::Tickle),
6 => Ok(PapFunction::CloseConn),
7 => Ok(PapFunction::CloseConnReply),
8 => Ok(PapFunction::SendStatus),
9 => Ok(PapFunction::Status),
_ => Err(PapError::UnknownFunction { code: value }),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PapPacket {
pub connection_id: u8,
pub function: PapFunction,
pub sequence_num: u16,
pub data: Vec<u8>,
}
impl PapPacket {
pub const MIN_HEADER_LEN: usize = 2;
pub fn parse(buf: &[u8]) -> Result<Self, PapError> {
if buf.len() < Self::MIN_HEADER_LEN {
return Err(PapError::InvalidSize {
expected: Self::MIN_HEADER_LEN,
found: buf.len(),
});
}
let connection_id = buf[0];
let function = PapFunction::try_from(buf[1])?;
let (sequence_num, data_start) = match function {
PapFunction::SendData | PapFunction::Data => {
if buf.len() < 4 {
return Err(PapError::InvalidSize {
expected: 4,
found: buf.len(),
});
}
(BigEndian::read_u16(&buf[2..4]), 4)
}
_ => (0, 2),
};
let data = buf[data_start..].to_vec();
Ok(Self {
connection_id,
function,
sequence_num,
data,
})
}
pub fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, PapError> {
let has_seq_num = matches!(self.function, PapFunction::SendData | PapFunction::Data);
let header_len = if has_seq_num { 4 } else { 2 };
let total_len = header_len + self.data.len();
if buf.len() < total_len {
return Err(PapError::InvalidSize {
expected: total_len,
found: buf.len(),
});
}
buf[0] = self.connection_id;
buf[1] = self.function as u8;
if has_seq_num {
BigEndian::write_u16(&mut buf[2..4], self.sequence_num);
buf[4..total_len].copy_from_slice(&self.data);
} else {
buf[2..total_len].copy_from_slice(&self.data);
}
Ok(total_len)
}
pub fn len(&self) -> usize {
let header_len = match self.function {
PapFunction::SendData | PapFunction::Data => 4,
_ => 2,
};
header_len + self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn to_atp_parts(&self) -> ([u8; 4], &[u8]) {
let mut user_bytes = [0u8; 4];
user_bytes[0] = self.connection_id;
user_bytes[1] = self.function as u8;
if matches!(self.function, PapFunction::SendData | PapFunction::Data) {
BigEndian::write_u16(&mut user_bytes[2..4], self.sequence_num);
}
(user_bytes, &self.data)
}
pub fn parse_from_atp(user_bytes: [u8; 4], data: &[u8]) -> Result<Self, PapError> {
let connection_id = user_bytes[0];
let function = PapFunction::try_from(user_bytes[1])?;
let sequence_num = match function {
PapFunction::SendData | PapFunction::Data => BigEndian::read_u16(&user_bytes[2..4]),
_ => 0,
};
Ok(Self {
connection_id,
function,
sequence_num,
data: data.to_vec(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_open_conn() {
let data: &[u8] = &[0x00, 0x01, 0x42, 0x00, 0x08];
let packet = PapPacket::parse(data).expect("failed to parse");
assert_eq!(packet.connection_id, 0);
assert_eq!(packet.function, PapFunction::OpenConn);
assert_eq!(packet.sequence_num, 0);
assert_eq!(packet.data, vec![0x42, 0x00, 0x08]);
}
#[test]
fn test_parse_send_data() {
let data: &[u8] = &[0x05, 0x03, 0x00, 0x01, b'H', b'e', b'l', b'l', b'o'];
let packet = PapPacket::parse(data).expect("failed to parse");
assert_eq!(packet.connection_id, 5);
assert_eq!(packet.function, PapFunction::SendData);
assert_eq!(packet.sequence_num, 1);
assert_eq!(packet.data, b"Hello");
}
#[test]
fn test_encode_open_conn_reply() {
let packet = PapPacket {
connection_id: 7,
function: PapFunction::OpenConnReply,
sequence_num: 0, data: vec![0x42, 0x00, 0x08, 0x00, 0x01], };
let mut buf = [0u8; 32];
let len = packet.to_bytes(&mut buf).expect("failed to encode");
assert_eq!(len, 7); assert_eq!(&buf[..len], &[0x07, 0x02, 0x42, 0x00, 0x08, 0x00, 0x01]);
}
#[test]
fn test_encode_data() {
let packet = PapPacket {
connection_id: 3,
function: PapFunction::Data,
sequence_num: 42,
data: b"PostScript data".to_vec(),
};
let mut buf = [0u8; 64];
let len = packet.to_bytes(&mut buf).expect("failed to encode");
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");
}
#[test]
fn test_round_trip_tickle() {
let original = PapPacket {
connection_id: 10,
function: PapFunction::Tickle,
sequence_num: 0,
data: vec![],
};
let mut buf = [0u8; 32];
let len = original.to_bytes(&mut buf).expect("failed to encode");
assert_eq!(len, 2);
let parsed = PapPacket::parse(&buf[..len]).expect("failed to parse");
assert_eq!(original, parsed);
}
#[test]
fn test_round_trip_with_data() {
let original = PapPacket {
connection_id: 15,
function: PapFunction::SendData,
sequence_num: 100,
data: b"Test print job data".to_vec(),
};
let mut buf = [0u8; 64];
let len = original.to_bytes(&mut buf).expect("failed to encode");
let parsed = PapPacket::parse(&buf[..len]).expect("failed to parse");
assert_eq!(original, parsed);
}
#[test]
fn test_invalid_function_code() {
let data: &[u8] = &[0x01, 0xFF];
let result = PapPacket::parse(data);
assert!(result.is_err());
match result {
Err(PapError::UnknownFunction { code: 0xFF }) => {}
_ => panic!("Expected UnknownFunction error"),
}
}
#[test]
fn test_buffer_too_small() {
let packet = PapPacket {
connection_id: 1,
function: PapFunction::Status,
sequence_num: 0,
data: vec![1, 2, 3, 4, 5],
};
let mut buf = [0u8; 4]; let result = packet.to_bytes(&mut buf);
assert!(result.is_err());
}
#[test]
fn test_atp_helpers() {
let original = PapPacket {
connection_id: 10,
function: PapFunction::SendData,
sequence_num: 12345,
data: b"Data Payload".to_vec(),
};
let (user_bytes, data) = original.to_atp_parts();
assert_eq!(user_bytes[0], 10);
assert_eq!(user_bytes[1], 3); assert_eq!(BigEndian::read_u16(&user_bytes[2..4]), 12345);
assert_eq!(data, b"Data Payload");
let parsed = PapPacket::parse_from_atp(user_bytes, data).expect("failed to parse from atp");
assert_eq!(original, parsed);
}
}