pub mod body;
pub mod header;
pub mod number;
pub mod payload;
pub mod ty;
pub use body::PacketBody;
pub use header::{
ACK_PACKET_HEADER_LEN, AckHeader, DATA_PACKET_HEADER_LEN, DataHeader, Flags,
LIVENESS_PACKET_HEADER_LEN, LOG_PACKET_HEADER_LEN, LogHeader, PacketHeader, PingHeader,
PongHeader,
};
pub use number::{PacketIndex, PacketKey};
pub use payload::PacketPayload;
pub use ty::PacketType;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Packet<'a> {
pub packet_type: PacketType,
pub body: PacketBody<'a>,
}
impl<'a> Packet<'a> {
#[must_use]
pub const fn from_parts(packet_type: PacketType, body: PacketBody<'a>) -> Self {
Self { packet_type, body }
}
#[must_use]
pub const fn header(self) -> PacketHeader {
match self.body {
PacketBody::Data { header, .. } => PacketHeader::from_data_header(header),
PacketBody::Log { header, .. } => PacketHeader::from_log_header(header),
PacketBody::Ack { header } => PacketHeader::ack(crate::core::PacketKey::new(
header.message_id,
header.packet_index,
)),
PacketBody::Ping { .. } => PacketHeader::ping(),
PacketBody::Pong { .. } => PacketHeader::pong(),
}
}
#[must_use]
pub const fn is_ack_eliciting(self) -> bool {
match self.body {
PacketBody::Data { header, .. } => header.is_ack_eliciting(),
PacketBody::Log { .. }
| PacketBody::Ack { .. }
| PacketBody::Ping { .. }
| PacketBody::Pong { .. } => false,
}
}
#[must_use]
pub const fn data_parts(header: DataHeader, payload: &'a [u8]) -> Self {
Self {
packet_type: PacketType::Data,
body: PacketBody::Data {
header,
payload: PacketPayload::new(payload),
},
}
}
#[must_use]
pub const fn log_parts(header: LogHeader, payload: &'a [u8]) -> Self {
Self {
packet_type: PacketType::Log,
body: PacketBody::Log {
header,
payload: PacketPayload::new(payload),
},
}
}
#[must_use]
pub const fn ack(header: AckHeader) -> Self {
Self {
packet_type: PacketType::Ack,
body: PacketBody::Ack { header },
}
}
#[must_use]
pub const fn ping(header: PingHeader) -> Self {
Self {
packet_type: PacketType::Ping,
body: PacketBody::Ping { header },
}
}
#[must_use]
pub const fn pong(header: PongHeader) -> Self {
Self {
packet_type: PacketType::Pong,
body: PacketBody::Pong { header },
}
}
#[must_use]
pub const fn payload_len(self) -> usize {
match self.body {
PacketBody::Data { payload, .. } | PacketBody::Log { payload, .. } => payload.len(),
PacketBody::Ack { .. } | PacketBody::Ping { .. } | PacketBody::Pong { .. } => 0,
}
}
#[must_use]
pub const fn is_empty(self) -> bool {
match self.body {
PacketBody::Data { payload, .. } | PacketBody::Log { payload, .. } => {
payload.is_empty()
}
PacketBody::Ack { .. } | PacketBody::Ping { .. } | PacketBody::Pong { .. } => true,
}
}
#[must_use]
pub const fn payload_bytes(self) -> &'a [u8] {
match self.body {
PacketBody::Data { payload, .. } | PacketBody::Log { payload, .. } => {
payload.as_bytes()
}
PacketBody::Ack { .. } | PacketBody::Ping { .. } | PacketBody::Pong { .. } => &[],
}
}
}
#[cfg(test)]
mod tests {
use super::{AckHeader, DataHeader, Flags, Packet, PacketBody, PacketIndex};
use crate::core::MessageId;
#[test]
fn packet_payload_contains_bytes() {
let payload = [1, 2, 3];
let header = DataHeader::new(
Flags::ACK_ELICITING,
MessageId::new(7),
PacketIndex::new(0),
3,
0,
);
let packet = Packet::data_parts(header, &payload);
assert_eq!(packet.payload_len(), 3);
assert!(!packet.is_empty());
assert_eq!(packet.payload_bytes(), payload);
assert!(packet.is_ack_eliciting());
}
#[test]
fn ack_packet_has_no_payload() {
let header = AckHeader::new(MessageId::new(7), PacketIndex::new(0));
let packet = Packet::ack(header);
assert_eq!(packet.payload_len(), 0);
assert!(packet.is_empty());
assert!(matches!(packet.body, PacketBody::Ack { .. }));
}
}