use serde::{Deserialize, Serialize};
pub use engineioxide_core::{Sid, Str};
use crate::Value;
#[derive(Debug, Clone, PartialEq)]
pub struct Packet {
pub inner: PacketData,
pub ns: Str,
}
impl Packet {
pub fn connect(ns: impl Into<Str>, value: Option<Value>) -> Self {
Self {
inner: PacketData::Connect(value),
ns: ns.into(),
}
}
pub fn disconnect(ns: impl Into<Str>) -> Self {
Self {
inner: PacketData::Disconnect,
ns: ns.into(),
}
}
}
impl Packet {
pub fn connect_error(ns: impl Into<Str>, message: impl Into<String>) -> Self {
Self {
inner: PacketData::ConnectError(message.into()),
ns: ns.into(),
}
}
pub fn event(ns: impl Into<Str>, data: Value) -> Self {
Self {
inner: match data {
Value::Str(_, Some(ref bins)) if !bins.is_empty() => {
PacketData::BinaryEvent(data, None)
}
_ => PacketData::Event(data, None),
},
ns: ns.into(),
}
}
pub fn ack(ns: impl Into<Str>, data: Value, ack: i64) -> Self {
Self {
inner: match data {
Value::Str(_, Some(ref bins)) if !bins.is_empty() => {
PacketData::BinaryAck(data, ack)
}
_ => PacketData::EventAck(data, ack),
},
ns: ns.into(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum PacketData {
Connect(Option<Value>),
Disconnect,
Event(Value, Option<i64>),
EventAck(Value, i64),
ConnectError(String),
BinaryEvent(Value, Option<i64>),
BinaryAck(Value, i64),
}
impl PacketData {
pub fn index(&self) -> usize {
match self {
PacketData::Connect(_) => 0,
PacketData::Disconnect => 1,
PacketData::Event(_, _) => 2,
PacketData::EventAck(_, _) => 3,
PacketData::ConnectError(_) => 4,
PacketData::BinaryEvent(_, _) => 5,
PacketData::BinaryAck(_, _) => 6,
}
}
pub fn set_ack_id(&mut self, ack_id: i64) {
match self {
PacketData::Event(_, ack) | PacketData::BinaryEvent(_, ack) => *ack = Some(ack_id),
_ => {}
};
}
pub fn is_binary(&self) -> bool {
matches!(
self,
PacketData::BinaryEvent(_, _) | PacketData::BinaryAck(_, _)
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConnectPacket {
pub sid: Sid,
}
impl Serialize for Packet {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[derive(Serialize)]
struct RawPacket<'a> {
ns: &'a Str,
r#type: u8,
data: Option<&'a Value>,
ack: Option<i64>,
error: Option<&'a String>,
}
let (r#type, data, ack, error) = match &self.inner {
PacketData::Connect(v) => (0, v.as_ref(), None, None),
PacketData::Disconnect => (1, None, None, None),
PacketData::Event(v, ack) => (2, Some(v), *ack, None),
PacketData::EventAck(v, ack) => (3, Some(v), Some(*ack), None),
PacketData::ConnectError(e) => (4, None, None, Some(e)),
PacketData::BinaryEvent(v, ack) => (5, Some(v), *ack, None),
PacketData::BinaryAck(v, ack) => (6, Some(v), Some(*ack), None),
};
let raw = RawPacket {
ns: &self.ns,
data,
ack,
error,
r#type,
};
raw.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Packet {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
struct RawPacket {
ns: Str,
r#type: u8,
data: Option<Value>,
ack: Option<i64>,
error: Option<String>,
}
let raw = RawPacket::deserialize(deserializer)?;
let err = |field| serde::de::Error::custom(format!("missing field: {}", field));
let inner = match raw.r#type {
0 => PacketData::Connect(raw.data),
1 => PacketData::Disconnect,
2 => PacketData::Event(raw.data.ok_or(err("data"))?, raw.ack),
3 => PacketData::EventAck(raw.data.ok_or(err("data"))?, raw.ack.ok_or(err("ack"))?),
4 => PacketData::ConnectError(raw.error.ok_or(err("error"))?),
5 => PacketData::BinaryEvent(raw.data.ok_or(err("data"))?, raw.ack),
6 => PacketData::BinaryAck(raw.data.ok_or(err("data"))?, raw.ack.ok_or(err("ack"))?),
i => return Err(serde::de::Error::custom(format!("invalid packet type {i}"))),
};
Ok(Self { inner, ns: raw.ns })
}
}
#[cfg(test)]
mod tests {
use std::collections::VecDeque;
use super::{Packet, PacketData, Value};
use bytes::Bytes;
#[test]
fn should_create_bin_packet_with_adjacent_binary() {
let val = Value::Str(
"test".into(),
Some(vec![Bytes::from_static(&[1, 2, 3])].into()),
);
assert!(matches!(
Packet::event("/", val.clone()).inner,
PacketData::BinaryEvent(v, None) if v == val));
assert!(matches!(
Packet::ack("/", val.clone(), 120).inner,
PacketData::BinaryAck(v, 120) if v == val));
}
#[test]
fn should_create_default_packet_with_base_data() {
let val = Value::Str("test".into(), None);
let val1 = Value::Bytes(Bytes::from_static(b"test"));
assert!(matches!(
Packet::event("/", val.clone()).inner,
PacketData::Event(v, None) if v == val));
assert!(matches!(
Packet::ack("/", val.clone(), 120).inner,
PacketData::EventAck(v, 120) if v == val));
assert!(matches!(
Packet::event("/", val1.clone()).inner,
PacketData::Event(v, None) if v == val1));
assert!(matches!(
Packet::ack("/", val1.clone(), 120).inner,
PacketData::EventAck(v, 120) if v == val1));
}
fn assert_serde_packet(packet: Packet) {
let serialized = serde_json::to_string(&packet).unwrap();
let deserialized: Packet = serde_json::from_str(&serialized).unwrap();
assert_eq!(packet, deserialized);
}
#[test]
fn packet_serde_connect() {
let packet = Packet {
ns: "/".into(),
inner: PacketData::Connect(Some(Value::Str("test_data".into(), None))),
};
assert_serde_packet(packet);
}
#[test]
fn packet_serde_disconnect() {
let packet = Packet {
ns: "/".into(),
inner: PacketData::Disconnect,
};
assert_serde_packet(packet);
}
#[test]
fn packet_serde_event() {
let packet = Packet {
ns: "/".into(),
inner: PacketData::Event(Value::Str("event_data".into(), None), None),
};
assert_serde_packet(packet);
let mut bins = VecDeque::new();
bins.push_back(Bytes::from_static(&[1, 2, 3, 4]));
bins.push_back(Bytes::from_static(&[1, 2, 3, 4]));
let packet = Packet {
ns: "/".into(),
inner: PacketData::Event(Value::Str("event_data".into(), Some(bins)), Some(12)),
};
assert_serde_packet(packet);
}
#[test]
fn packet_serde_event_ack() {
let packet = Packet {
ns: "/".into(),
inner: PacketData::EventAck(Value::Str("event_ack_data".into(), None), 42),
};
assert_serde_packet(packet);
}
#[test]
fn packet_serde_connect_error() {
let packet = Packet {
ns: "/".into(),
inner: PacketData::ConnectError("connection_error".into()),
};
assert_serde_packet(packet);
}
#[test]
fn packet_serde_binary_event() {
let packet = Packet {
ns: "/".into(),
inner: PacketData::BinaryEvent(Value::Str("binary_event_data".into(), None), None),
};
assert_serde_packet(packet);
let mut bins = VecDeque::new();
bins.push_back(Bytes::from_static(&[1, 2, 3, 4]));
bins.push_back(Bytes::from_static(&[1, 2, 3, 4]));
let packet = Packet {
ns: "/".into(),
inner: PacketData::BinaryEvent(Value::Str("event_data".into(), Some(bins)), Some(12)),
};
assert_serde_packet(packet);
}
#[test]
fn packet_serde_binary_ack() {
let packet = Packet {
ns: "/".into(),
inner: PacketData::BinaryAck(Value::Str("binary_ack_data".into(), None), 99),
};
assert_serde_packet(packet);
let mut bins = VecDeque::new();
bins.push_back(Bytes::from_static(&[1, 2, 3, 4]));
bins.push_back(Bytes::from_static(&[1, 2, 3, 4]));
let packet = Packet {
ns: "/".into(),
inner: PacketData::BinaryAck(Value::Str("binary_ack_data".into(), Some(bins)), 99),
};
assert_serde_packet(packet);
}
}