use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
#[repr(u8)]
pub enum ProtocolVersion {
#[default]
V1 = 1,
V2 = 2,
}
impl ProtocolVersion {
pub fn from_query_param(value: Option<u8>) -> Self {
match value {
Some(2) => Self::V2,
_ => Self::V1,
}
}
#[inline]
pub const fn event_prefix(&self) -> &'static str {
match self {
Self::V1 => "pusher:",
Self::V2 => "sockudo:",
}
}
#[inline]
pub const fn internal_prefix(&self) -> &'static str {
match self {
Self::V1 => "pusher_internal:",
Self::V2 => "sockudo_internal:",
}
}
#[inline]
pub const fn is_serial_native(&self) -> bool {
matches!(self, Self::V2)
}
#[inline]
pub const fn is_message_id_native(&self) -> bool {
matches!(self, Self::V2)
}
#[inline]
pub const fn is_recovery_native(&self) -> bool {
matches!(self, Self::V2)
}
#[inline]
pub fn wire_event(&self, canonical: &str) -> String {
format!("{}{}", self.event_prefix(), canonical)
}
#[inline]
pub fn wire_internal_event(&self, canonical: &str) -> String {
format!("{}{}", self.internal_prefix(), canonical)
}
pub fn parse_event_name<'a>(&self, wire: &'a str) -> Option<&'a str> {
wire.strip_prefix(self.event_prefix())
.or_else(|| wire.strip_prefix(self.internal_prefix()))
}
pub fn parse_any_protocol_event(wire: &str) -> Option<(&str, bool)> {
if let Some(canonical) = wire.strip_prefix("pusher:") {
Some((canonical, false))
} else if let Some(canonical) = wire.strip_prefix("pusher_internal:") {
Some((canonical, true))
} else if let Some(canonical) = wire.strip_prefix("sockudo:") {
Some((canonical, false))
} else if let Some(canonical) = wire.strip_prefix("sockudo_internal:") {
Some((canonical, true))
} else {
None
}
}
pub fn rewrite_event_prefix(&self, event: &str) -> String {
if let Some((canonical, is_internal)) = Self::parse_any_protocol_event(event) {
if is_internal {
self.wire_internal_event(canonical)
} else {
self.wire_event(canonical)
}
} else {
event.to_string()
}
}
}
pub const CANONICAL_CONNECTION_ESTABLISHED: &str = "connection_established";
pub const CANONICAL_ERROR: &str = "error";
pub const CANONICAL_PING: &str = "ping";
pub const CANONICAL_PONG: &str = "pong";
pub const CANONICAL_SUBSCRIBE: &str = "subscribe";
pub const CANONICAL_UNSUBSCRIBE: &str = "unsubscribe";
pub const CANONICAL_SIGNIN: &str = "signin";
pub const CANONICAL_SIGNIN_SUCCESS: &str = "signin_success";
pub const CANONICAL_CACHE_MISS: &str = "cache_miss";
pub const CANONICAL_SUBSCRIPTION_SUCCEEDED: &str = "subscription_succeeded";
pub const CANONICAL_SUBSCRIPTION_ERROR: &str = "subscription_error";
pub const CANONICAL_MEMBER_ADDED: &str = "member_added";
pub const CANONICAL_MEMBER_REMOVED: &str = "member_removed";
pub const CANONICAL_ENABLE_DELTA_COMPRESSION: &str = "enable_delta_compression";
pub const CANONICAL_DELTA_COMPRESSION_ENABLED: &str = "delta_compression_enabled";
pub const CANONICAL_DELTA: &str = "delta";
pub const CANONICAL_DELTA_CACHE_SYNC: &str = "delta_cache_sync";
pub const CANONICAL_DELTA_SYNC_ERROR: &str = "delta_sync_error";
pub const CANONICAL_RESUME: &str = "resume";
pub const CANONICAL_RESUME_SUCCESS: &str = "resume_success";
pub const CANONICAL_RESUME_FAILED: &str = "resume_failed";
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_v1_prefix() {
assert_eq!(ProtocolVersion::V1.event_prefix(), "pusher:");
assert_eq!(ProtocolVersion::V1.internal_prefix(), "pusher_internal:");
}
#[test]
fn test_v2_prefix() {
assert_eq!(ProtocolVersion::V2.event_prefix(), "sockudo:");
assert_eq!(ProtocolVersion::V2.internal_prefix(), "sockudo_internal:");
}
#[test]
fn test_wire_event() {
assert_eq!(
ProtocolVersion::V1.wire_event("subscribe"),
"pusher:subscribe"
);
assert_eq!(
ProtocolVersion::V2.wire_event("subscribe"),
"sockudo:subscribe"
);
}
#[test]
fn test_wire_internal_event() {
assert_eq!(
ProtocolVersion::V1.wire_internal_event("subscription_succeeded"),
"pusher_internal:subscription_succeeded"
);
assert_eq!(
ProtocolVersion::V2.wire_internal_event("subscription_succeeded"),
"sockudo_internal:subscription_succeeded"
);
}
#[test]
fn test_parse_event_name() {
assert_eq!(
ProtocolVersion::V1.parse_event_name("pusher:ping"),
Some("ping")
);
assert_eq!(ProtocolVersion::V1.parse_event_name("sockudo:ping"), None);
assert_eq!(
ProtocolVersion::V2.parse_event_name("sockudo:ping"),
Some("ping")
);
assert_eq!(ProtocolVersion::V2.parse_event_name("pusher:ping"), None);
}
#[test]
fn test_parse_any_protocol_event() {
assert_eq!(
ProtocolVersion::parse_any_protocol_event("pusher:subscribe"),
Some(("subscribe", false))
);
assert_eq!(
ProtocolVersion::parse_any_protocol_event("sockudo:subscribe"),
Some(("subscribe", false))
);
assert_eq!(
ProtocolVersion::parse_any_protocol_event("pusher_internal:member_added"),
Some(("member_added", true))
);
assert_eq!(
ProtocolVersion::parse_any_protocol_event("sockudo_internal:member_added"),
Some(("member_added", true))
);
assert_eq!(
ProtocolVersion::parse_any_protocol_event("client-event"),
None
);
}
#[test]
fn test_rewrite_event_prefix() {
assert_eq!(
ProtocolVersion::V2.rewrite_event_prefix("pusher:subscribe"),
"sockudo:subscribe"
);
assert_eq!(
ProtocolVersion::V2.rewrite_event_prefix("pusher_internal:member_added"),
"sockudo_internal:member_added"
);
assert_eq!(
ProtocolVersion::V1.rewrite_event_prefix("sockudo:subscribe"),
"pusher:subscribe"
);
assert_eq!(
ProtocolVersion::V2.rewrite_event_prefix("client-my-event"),
"client-my-event"
);
}
#[test]
fn test_from_query_param() {
assert_eq!(ProtocolVersion::from_query_param(None), ProtocolVersion::V1);
assert_eq!(
ProtocolVersion::from_query_param(Some(1)),
ProtocolVersion::V1
);
assert_eq!(
ProtocolVersion::from_query_param(Some(2)),
ProtocolVersion::V2
);
assert_eq!(
ProtocolVersion::from_query_param(Some(99)),
ProtocolVersion::V1
);
}
#[test]
fn test_v2_native_features() {
assert!(!ProtocolVersion::V1.is_serial_native());
assert!(!ProtocolVersion::V1.is_message_id_native());
assert!(!ProtocolVersion::V1.is_recovery_native());
assert!(ProtocolVersion::V2.is_serial_native());
assert!(ProtocolVersion::V2.is_message_id_native());
assert!(ProtocolVersion::V2.is_recovery_native());
}
}