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}