use serde_json::Value; #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum PacketType { Connect = 0, Disconnect = 1, Event = 2, Ack = 3, ConnectError = 4, BinaryEvent = 5, BinaryAck = 6, } impl TryFrom for PacketType { type Error = PacketError; fn try_from(value: u8) -> Result { match value { 0 => Ok(Self::Connect), 1 => Ok(Self::Disconnect), 2 => Ok(Self::Event), 3 => Ok(Self::Ack), 4 => Ok(Self::ConnectError), 5 => Ok(Self::BinaryEvent), 6 => Ok(Self::BinaryAck), _ => Err(PacketError::InvalidType(value)), } } } impl TryFrom for PacketType { type Error = PacketError; fn try_from(value: char) -> Result { match value { '0' => Ok(Self::Connect), '1' => Ok(Self::Disconnect), '2' => Ok(Self::Event), '3' => Ok(Self::Ack), '4' => Ok(Self::ConnectError), '5' => Ok(Self::BinaryEvent), '6' => Ok(Self::BinaryAck), _ => Err(PacketError::InvalidTypeChar(value)), } } } #[derive(Debug, Clone)] pub struct Packet { pub packet_type: PacketType, pub namespace: String, pub data: Option, pub id: Option, pub attachments: Vec>, /// Expected number of binary attachments (set during decode for binary packets). /// Used to validate attachment count before assembling the full packet. pub expected_attachments: Option, } impl Packet { pub fn connect(namespace: impl Into, data: Option) -> Self { Self { packet_type: PacketType::Connect, namespace: namespace.into(), data, id: None, attachments: Vec::new(), expected_attachments: None, } } pub fn disconnect(namespace: impl Into) -> Self { Self { packet_type: PacketType::Disconnect, namespace: namespace.into(), data: None, id: None, attachments: Vec::new(), expected_attachments: None, } } pub fn event(namespace: impl Into, data: Value, id: Option) -> Self { Self { packet_type: PacketType::Event, namespace: namespace.into(), data: Some(data), id, attachments: Vec::new(), expected_attachments: None, } } pub fn ack(namespace: impl Into, data: Value, id: u64) -> Self { Self { packet_type: PacketType::Ack, namespace: namespace.into(), data: Some(data), id: Some(id), attachments: Vec::new(), expected_attachments: None, } } pub fn connect_error(namespace: impl Into, message: impl Into) -> Self { Self { packet_type: PacketType::ConnectError, namespace: namespace.into(), data: Some(serde_json::json!({ "message": message.into() })), id: None, attachments: Vec::new(), expected_attachments: None, } } pub fn binary_event( namespace: impl Into, data: Value, id: Option, attachments: Vec>, ) -> Self { Self { packet_type: PacketType::BinaryEvent, namespace: namespace.into(), data: Some(data), id, attachments, expected_attachments: None, } } pub fn binary_ack( namespace: impl Into, data: Value, id: u64, attachments: Vec>, ) -> Self { Self { packet_type: PacketType::BinaryAck, namespace: namespace.into(), data: Some(data), id: Some(id), attachments, expected_attachments: None, } } pub fn has_binary(&self) -> bool { !self.attachments.is_empty() } pub fn attachment_count(&self) -> usize { self.attachments.len() } } #[derive(Debug, thiserror::Error)] pub enum PacketError { #[error("invalid packet type: {0}")] InvalidType(u8), #[error("invalid packet type char: {0}")] InvalidTypeChar(char), #[error("empty packet")] Empty, #[error("invalid format: {0}")] InvalidFormat(String), #[error("json error: {0}")] Json(#[from] serde_json::Error), #[error("missing namespace")] MissingNamespace, #[error("invalid attachment count")] InvalidAttachmentCount, }