1use serde::de::DeserializeOwned;
2use serde::{Deserialize, Serialize};
3use serde_json::{Map, Value};
4use thiserror::Error;
5
6pub type PondMessage = Map<String, Value>;
7pub type PondPresence = Map<String, Value>;
8pub type PondAssigns = Map<String, Value>;
9pub type JoinParams = Map<String, Value>;
10
11pub trait PondEvent {
12 type Payload: Serialize + DeserializeOwned + Send + Sync + 'static;
13 type Response: Serialize + DeserializeOwned + Send + Sync + 'static;
14
15 const NAME: &'static str;
16}
17
18pub trait PondSchema {
19 type Presence: Serialize + DeserializeOwned + Send + Sync + 'static;
20 type Assigns: Serialize + DeserializeOwned + Send + Sync + 'static;
21 type JoinParams: Serialize + DeserializeOwned + Send + Sync + 'static;
22}
23
24pub fn to_pond_value<T>(value: &T) -> serde_json::Result<Value>
25where
26 T: Serialize + ?Sized,
27{
28 serde_json::to_value(value)
29}
30
31pub fn to_pond_map<T>(value: &T) -> serde_json::Result<Map<String, Value>>
32where
33 T: Serialize + ?Sized,
34{
35 match serde_json::to_value(value)? {
36 Value::Object(map) => Ok(map),
37 Value::Null => Ok(Map::new()),
38 value => {
39 let mut map = Map::new();
40 map.insert("value".to_owned(), value);
41 Ok(map)
42 }
43 }
44}
45
46pub fn from_pond_value<T>(value: Value) -> serde_json::Result<T>
47where
48 T: DeserializeOwned,
49{
50 serde_json::from_value(value)
51}
52
53pub fn from_pond_map<T>(value: Map<String, Value>) -> serde_json::Result<T>
54where
55 T: DeserializeOwned,
56{
57 serde_json::from_value(Value::Object(value))
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
61#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
62pub enum PresenceEventType {
63 Join,
64 Leave,
65 Update,
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
69#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
70pub enum ClientAction {
71 JoinChannel,
72 LeaveChannel,
73 Broadcast,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
77#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
78pub enum ServerAction {
79 Presence,
80 System,
81 Broadcast,
82 Error,
83 Connect,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
87#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
88pub enum ChannelState {
89 Idle,
90 Joining,
91 Joined,
92 Stalled,
93 Closed,
94 Declined,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
98#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
99pub enum ErrorType {
100 UnauthorizedConnection,
101 UnauthorizedJoinRequest,
102 UnauthorizedBroadcast,
103 InvalidMessage,
104 HandlerNotFound,
105 PresenceError,
106 ChannelError,
107 EndpointError,
108 InternalServerError,
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
112#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
113pub enum EventName {
114 Acknowledge,
115 ExitAcknowledge,
116 Connection,
117 InternalError,
118 NotFound,
119 Unauthorized,
120}
121
122#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
123pub struct ClientMessage {
124 pub event: String,
125 #[serde(rename = "requestId")]
126 pub request_id: String,
127 #[serde(rename = "channelName")]
128 pub channel_name: String,
129 #[serde(default)]
130 pub payload: PondMessage,
131 pub action: ClientAction,
132}
133
134#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
135pub struct ServerMessage {
136 pub event: String,
137 #[serde(rename = "requestId")]
138 pub request_id: String,
139 #[serde(rename = "channelName")]
140 pub channel_name: String,
141 #[serde(default)]
142 pub payload: PondMessage,
143 pub action: ServerAction,
144}
145
146#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
147pub struct PresencePayload {
148 #[serde(default)]
149 pub presence: Vec<PondPresence>,
150 #[serde(default)]
151 pub changed: PondPresence,
152}
153
154#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
155pub struct PresenceMessage {
156 #[serde(rename = "requestId")]
157 pub request_id: String,
158 #[serde(rename = "channelName")]
159 pub channel_name: String,
160 pub event: PresenceEventType,
161 pub action: PresenceAction,
162 pub payload: PresencePayload,
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
166#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
167pub enum PresenceAction {
168 Presence,
169}
170
171#[derive(Debug, Clone, PartialEq)]
172pub enum ChannelEvent {
173 Message(ServerMessage),
174 Presence(PresenceMessage),
175}
176
177#[derive(Debug, Clone, Error, PartialEq, Eq)]
178#[error("{path}: {message}")]
179pub struct ValidationError {
180 pub path: String,
181 pub message: String,
182}
183
184impl ValidationError {
185 pub fn new(path: impl Into<String>, message: impl Into<String>) -> Self {
186 Self {
187 path: path.into(),
188 message: message.into(),
189 }
190 }
191}
192
193pub fn uuid() -> String {
194 uuid::Uuid::new_v4().to_string()
195}
196
197pub fn parse_client_message(data: &str) -> Result<ClientMessage, ValidationError> {
198 let value = parse_object(data, "clientMessage")?;
199 serde_json::from_value(value).map_err(|e| ValidationError::new("clientMessage", e.to_string()))
200}
201
202pub fn parse_server_message(data: &str) -> Result<ServerMessage, ValidationError> {
203 let value = parse_object(data, "serverMessage")?;
204 serde_json::from_value(value).map_err(|e| ValidationError::new("serverMessage", e.to_string()))
205}
206
207pub fn parse_channel_event(data: &str) -> Result<ChannelEvent, ValidationError> {
208 let value = parse_object(data, "channelEvent")?;
209 let action = value
210 .get("action")
211 .and_then(Value::as_str)
212 .ok_or_else(|| ValidationError::new("action", "Missing required field"))?;
213 if action == "PRESENCE" {
214 let msg: PresenceMessage = serde_json::from_value(value)
215 .map_err(|e| ValidationError::new("presenceMessage", e.to_string()))?;
216 Ok(ChannelEvent::Presence(msg))
217 } else {
218 let msg: ServerMessage = serde_json::from_value(value)
219 .map_err(|e| ValidationError::new("serverMessage", e.to_string()))?;
220 Ok(ChannelEvent::Message(msg))
221 }
222}
223
224fn parse_object(data: &str, root: &str) -> Result<Value, ValidationError> {
225 let value: Value =
226 serde_json::from_str(data).map_err(|e| ValidationError::new(root, e.to_string()))?;
227 if value.is_object() {
228 Ok(value)
229 } else {
230 Err(ValidationError::new(root, "Expected object"))
231 }
232}
233
234pub fn message_to_json(message: &ServerMessage) -> serde_json::Result<String> {
235 serde_json::to_string(message)
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn parses_client_message_with_camel_case_fields() {
244 let msg = parse_client_message(
245 r#"{"action":"BROADCAST","event":"message","channelName":"/chat/1","requestId":"r1","payload":{"text":"hi"}}"#,
246 )
247 .unwrap();
248 assert_eq!(msg.action, ClientAction::Broadcast);
249 assert_eq!(msg.channel_name, "/chat/1");
250 }
251
252 #[test]
253 fn routes_presence_channel_event() {
254 let ev = parse_channel_event(
255 r#"{"action":"PRESENCE","event":"JOIN","channelName":"/chat/1","requestId":"r1","payload":{"presence":[{"id":"u1"}],"changed":{"id":"u1"}}}"#,
256 )
257 .unwrap();
258 assert!(matches!(ev, ChannelEvent::Presence(_)));
259 }
260}