Skip to main content

realtime/protocol/
mod.rs

1use chrono::Utc;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use uuid::Uuid;
5
6pub const DEFAULT_EVENT: &str = "message";
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ErrorPayload {
10    pub code: String,
11    pub message: String,
12}
13
14impl ErrorPayload {
15    pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
16        Self {
17            code: code.into(),
18            message: message.into(),
19        }
20    }
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24#[serde(tag = "op", rename_all = "snake_case")]
25pub enum ClientFrame {
26    ChannelJoin {
27        id: String,
28        channel: String,
29        #[serde(default)]
30        ts: Option<i64>,
31    },
32    ChannelLeave {
33        id: String,
34        channel: String,
35        #[serde(default)]
36        ts: Option<i64>,
37    },
38    ChannelEmit {
39        id: String,
40        channel: String,
41        event: String,
42        #[serde(default)]
43        data: Value,
44        #[serde(default)]
45        ts: Option<i64>,
46    },
47    Ping {
48        id: String,
49        #[serde(default)]
50        ts: Option<i64>,
51    },
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55#[serde(tag = "op", rename_all = "snake_case")]
56pub enum ServerFrame {
57    Connected {
58        id: String,
59        conn_id: String,
60        user_id: String,
61        ts: i64,
62    },
63    Joined {
64        id: String,
65        channel: String,
66        ts: i64,
67    },
68    Left {
69        id: String,
70        channel: String,
71        ts: i64,
72    },
73    Event {
74        id: String,
75        channel: String,
76        event: String,
77        data: Value,
78        from_user: Option<String>,
79        ts: i64,
80    },
81    Ack {
82        id: String,
83        for_id: String,
84        ok: bool,
85        error: Option<ErrorPayload>,
86        ts: i64,
87    },
88    Pong {
89        id: String,
90        ts: i64,
91    },
92    Error {
93        id: String,
94        error: ErrorPayload,
95        ts: i64,
96    },
97}
98
99impl ServerFrame {
100    pub fn connected(conn_id: impl Into<String>, user_id: impl Into<String>) -> Self {
101        Self::Connected {
102            id: random_id(),
103            conn_id: conn_id.into(),
104            user_id: user_id.into(),
105            ts: now_unix_i64(),
106        }
107    }
108
109    pub fn event(
110        channel: impl Into<String>,
111        event: impl Into<String>,
112        data: Value,
113        from_user: Option<String>,
114    ) -> Self {
115        Self::Event {
116            id: random_id(),
117            channel: channel.into(),
118            event: event.into(),
119            data,
120            from_user,
121            ts: now_unix_i64(),
122        }
123    }
124
125    pub fn ack_ok(for_id: impl Into<String>) -> Self {
126        Self::Ack {
127            id: random_id(),
128            for_id: for_id.into(),
129            ok: true,
130            error: None,
131            ts: now_unix_i64(),
132        }
133    }
134
135    pub fn ack_err(for_id: impl Into<String>, code: &str, message: &str) -> Self {
136        Self::Ack {
137            id: random_id(),
138            for_id: for_id.into(),
139            ok: false,
140            error: Some(ErrorPayload::new(code, message)),
141            ts: now_unix_i64(),
142        }
143    }
144
145    pub fn pong(for_id: impl Into<String>) -> Self {
146        Self::Pong {
147            id: for_id.into(),
148            ts: now_unix_i64(),
149        }
150    }
151
152    pub fn error(code: &str, message: &str) -> Self {
153        Self::Error {
154            id: random_id(),
155            error: ErrorPayload::new(code, message),
156            ts: now_unix_i64(),
157        }
158    }
159}
160
161fn random_id() -> String {
162    Uuid::new_v4().to_string()
163}
164
165fn now_unix_i64() -> i64 {
166    Utc::now().timestamp()
167}