sockudo_protocol/
protocol_version.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
10#[repr(u8)]
11pub enum ProtocolVersion {
12 #[default]
13 V1 = 1,
14 V2 = 2,
15}
16
17impl ProtocolVersion {
18 pub fn from_query_param(value: Option<u8>) -> Self {
21 match value {
22 Some(2) => Self::V2,
23 _ => Self::V1,
24 }
25 }
26
27 #[inline]
29 pub const fn event_prefix(&self) -> &'static str {
30 match self {
31 Self::V1 => "pusher:",
32 Self::V2 => "sockudo:",
33 }
34 }
35
36 #[inline]
38 pub const fn internal_prefix(&self) -> &'static str {
39 match self {
40 Self::V1 => "pusher_internal:",
41 Self::V2 => "sockudo_internal:",
42 }
43 }
44
45 #[inline]
47 pub const fn is_serial_native(&self) -> bool {
48 matches!(self, Self::V2)
49 }
50
51 #[inline]
53 pub const fn is_message_id_native(&self) -> bool {
54 matches!(self, Self::V2)
55 }
56
57 #[inline]
59 pub const fn is_recovery_native(&self) -> bool {
60 matches!(self, Self::V2)
61 }
62
63 #[inline]
66 pub fn wire_event(&self, canonical: &str) -> String {
67 format!("{}{}", self.event_prefix(), canonical)
68 }
69
70 #[inline]
73 pub fn wire_internal_event(&self, canonical: &str) -> String {
74 format!("{}{}", self.internal_prefix(), canonical)
75 }
76
77 pub fn parse_event_name<'a>(&self, wire: &'a str) -> Option<&'a str> {
80 wire.strip_prefix(self.event_prefix())
81 .or_else(|| wire.strip_prefix(self.internal_prefix()))
82 }
83
84 pub fn parse_any_protocol_event(wire: &str) -> Option<(&str, bool)> {
87 if let Some(canonical) = wire.strip_prefix("pusher:") {
88 Some((canonical, false))
89 } else if let Some(canonical) = wire.strip_prefix("pusher_internal:") {
90 Some((canonical, true))
91 } else if let Some(canonical) = wire.strip_prefix("sockudo:") {
92 Some((canonical, false))
93 } else if let Some(canonical) = wire.strip_prefix("sockudo_internal:") {
94 Some((canonical, true))
95 } else {
96 None
97 }
98 }
99
100 pub fn rewrite_event_prefix(&self, event: &str) -> String {
103 if let Some((canonical, is_internal)) = Self::parse_any_protocol_event(event) {
104 if is_internal {
105 self.wire_internal_event(canonical)
106 } else {
107 self.wire_event(canonical)
108 }
109 } else {
110 event.to_string()
111 }
112 }
113}
114
115pub const CANONICAL_CONNECTION_ESTABLISHED: &str = "connection_established";
117pub const CANONICAL_ERROR: &str = "error";
118pub const CANONICAL_PING: &str = "ping";
119pub const CANONICAL_PONG: &str = "pong";
120pub const CANONICAL_SUBSCRIBE: &str = "subscribe";
121pub const CANONICAL_UNSUBSCRIBE: &str = "unsubscribe";
122pub const CANONICAL_SIGNIN: &str = "signin";
123pub const CANONICAL_SIGNIN_SUCCESS: &str = "signin_success";
124pub const CANONICAL_CACHE_MISS: &str = "cache_miss";
125
126pub const CANONICAL_SUBSCRIPTION_SUCCEEDED: &str = "subscription_succeeded";
128pub const CANONICAL_SUBSCRIPTION_ERROR: &str = "subscription_error";
129pub const CANONICAL_MEMBER_ADDED: &str = "member_added";
130pub const CANONICAL_MEMBER_REMOVED: &str = "member_removed";
131
132pub const CANONICAL_ENABLE_DELTA_COMPRESSION: &str = "enable_delta_compression";
134pub const CANONICAL_DELTA_COMPRESSION_ENABLED: &str = "delta_compression_enabled";
135pub const CANONICAL_DELTA: &str = "delta";
136pub const CANONICAL_DELTA_CACHE_SYNC: &str = "delta_cache_sync";
137pub const CANONICAL_DELTA_SYNC_ERROR: &str = "delta_sync_error";
138
139pub const CANONICAL_RESUME: &str = "resume";
141pub const CANONICAL_RESUME_SUCCESS: &str = "resume_success";
142pub const CANONICAL_RESUME_FAILED: &str = "resume_failed";
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_v1_prefix() {
150 assert_eq!(ProtocolVersion::V1.event_prefix(), "pusher:");
151 assert_eq!(ProtocolVersion::V1.internal_prefix(), "pusher_internal:");
152 }
153
154 #[test]
155 fn test_v2_prefix() {
156 assert_eq!(ProtocolVersion::V2.event_prefix(), "sockudo:");
157 assert_eq!(ProtocolVersion::V2.internal_prefix(), "sockudo_internal:");
158 }
159
160 #[test]
161 fn test_wire_event() {
162 assert_eq!(
163 ProtocolVersion::V1.wire_event("subscribe"),
164 "pusher:subscribe"
165 );
166 assert_eq!(
167 ProtocolVersion::V2.wire_event("subscribe"),
168 "sockudo:subscribe"
169 );
170 }
171
172 #[test]
173 fn test_wire_internal_event() {
174 assert_eq!(
175 ProtocolVersion::V1.wire_internal_event("subscription_succeeded"),
176 "pusher_internal:subscription_succeeded"
177 );
178 assert_eq!(
179 ProtocolVersion::V2.wire_internal_event("subscription_succeeded"),
180 "sockudo_internal:subscription_succeeded"
181 );
182 }
183
184 #[test]
185 fn test_parse_event_name() {
186 assert_eq!(
187 ProtocolVersion::V1.parse_event_name("pusher:ping"),
188 Some("ping")
189 );
190 assert_eq!(ProtocolVersion::V1.parse_event_name("sockudo:ping"), None);
191 assert_eq!(
192 ProtocolVersion::V2.parse_event_name("sockudo:ping"),
193 Some("ping")
194 );
195 assert_eq!(ProtocolVersion::V2.parse_event_name("pusher:ping"), None);
196 }
197
198 #[test]
199 fn test_parse_any_protocol_event() {
200 assert_eq!(
201 ProtocolVersion::parse_any_protocol_event("pusher:subscribe"),
202 Some(("subscribe", false))
203 );
204 assert_eq!(
205 ProtocolVersion::parse_any_protocol_event("sockudo:subscribe"),
206 Some(("subscribe", false))
207 );
208 assert_eq!(
209 ProtocolVersion::parse_any_protocol_event("pusher_internal:member_added"),
210 Some(("member_added", true))
211 );
212 assert_eq!(
213 ProtocolVersion::parse_any_protocol_event("sockudo_internal:member_added"),
214 Some(("member_added", true))
215 );
216 assert_eq!(
217 ProtocolVersion::parse_any_protocol_event("client-event"),
218 None
219 );
220 }
221
222 #[test]
223 fn test_rewrite_event_prefix() {
224 assert_eq!(
226 ProtocolVersion::V2.rewrite_event_prefix("pusher:subscribe"),
227 "sockudo:subscribe"
228 );
229 assert_eq!(
230 ProtocolVersion::V2.rewrite_event_prefix("pusher_internal:member_added"),
231 "sockudo_internal:member_added"
232 );
233
234 assert_eq!(
236 ProtocolVersion::V1.rewrite_event_prefix("sockudo:subscribe"),
237 "pusher:subscribe"
238 );
239
240 assert_eq!(
242 ProtocolVersion::V2.rewrite_event_prefix("client-my-event"),
243 "client-my-event"
244 );
245 }
246
247 #[test]
248 fn test_from_query_param() {
249 assert_eq!(ProtocolVersion::from_query_param(None), ProtocolVersion::V1);
250 assert_eq!(
251 ProtocolVersion::from_query_param(Some(1)),
252 ProtocolVersion::V1
253 );
254 assert_eq!(
255 ProtocolVersion::from_query_param(Some(2)),
256 ProtocolVersion::V2
257 );
258 assert_eq!(
259 ProtocolVersion::from_query_param(Some(99)),
260 ProtocolVersion::V1
261 );
262 }
263
264 #[test]
265 fn test_v2_native_features() {
266 assert!(!ProtocolVersion::V1.is_serial_native());
267 assert!(!ProtocolVersion::V1.is_message_id_native());
268 assert!(!ProtocolVersion::V1.is_recovery_native());
269
270 assert!(ProtocolVersion::V2.is_serial_native());
271 assert!(ProtocolVersion::V2.is_message_id_native());
272 assert!(ProtocolVersion::V2.is_recovery_native());
273 }
274}