1use std::borrow::Cow;
4use std::fmt::{Debug, Display};
5
6use serde::de::DeserializeOwned;
7use serde::{Deserialize, Serialize};
8
9use crate::client::Id;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13#[non_exhaustive]
14pub struct Message<P> {
15 pub join_reference: Option<String>,
22 pub message_reference: Option<String>,
26 pub topic_name: String,
29 pub event_name: String,
32 pub payload: P,
35}
36
37impl<P> Message<P> {
38 pub(crate) fn info(&self) -> String {
39 format!(
40 "[{:?}, {:?}, {:?}, {:?}, <payload>]",
41 self.join_reference, self.message_reference, self.topic_name, self.event_name
42 )
43 }
44}
45
46impl<'a, P> From<ChannelMsg<'a, P>> for Message<P> {
47 fn from(value: ChannelMsg<'a, P>) -> Self {
48 Self {
49 join_reference: value.join_reference.map(Cow::into),
50 message_reference: value.message_reference.map(Cow::into),
51 topic_name: value.topic_name.into(),
52 event_name: value.event_name.into(),
53 payload: value.payload,
54 }
55 }
56}
57
58impl Message<serde_json::Value> {
59 pub fn deserialize_payload<P>(self) -> Result<Message<P>, serde_json::error::Error>
64 where
65 P: DeserializeOwned,
66 {
67 let payload = serde_json::from_value(self.payload)?;
68
69 Ok(Message {
70 join_reference: self.join_reference,
71 message_reference: self.message_reference,
72 topic_name: self.topic_name,
73 event_name: self.event_name,
74 payload,
75 })
76 }
77}
78
79impl<P> Display for Message<P>
80where
81 P: Serialize + Debug,
82{
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 write!(f, "[")?;
85 ser_or_debug(&self.join_reference, f)?;
86 write!(f, ", ")?;
87 ser_or_debug(&self.message_reference, f)?;
88 write!(f, ", ")?;
89 ser_or_debug(&self.topic_name, f)?;
90 write!(f, ", ")?;
91 ser_or_debug(&self.event_name, f)?;
92 write!(f, ", ")?;
93 ser_or_debug(&self.payload, f)?;
94 write!(f, "]")
95 }
96}
97
98fn ser_or_debug<T>(v: &T, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
99where
100 T: Serialize + Debug,
101{
102 if let Ok(s) = serde_json::to_string(v) {
103 write!(f, "{s}")
104 } else {
105 write!(f, "{v:?}")
106 }
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
110pub(crate) struct ChannelMsg<'a, P> {
111 pub(crate) join_reference: Option<Cow<'a, str>>,
112 pub(crate) message_reference: Option<Cow<'a, str>>,
113 pub(crate) topic_name: Cow<'a, str>,
114 pub(crate) event_name: Cow<'a, str>,
115 pub(crate) payload: P,
116}
117
118impl<'a, P> ChannelMsg<'a, P> {
119 pub(crate) fn new(
120 join_reference: Option<Id>,
121 message_reference: Option<Id>,
122 topic_name: &'a str,
123 event_name: &'a str,
124 payload: P,
125 ) -> Self {
126 Self {
127 join_reference: join_reference.map(|id| Cow::Owned(id.to_string())),
128 message_reference: message_reference.map(|id| Cow::Owned(id.to_string())),
129 topic_name: Cow::Borrowed(topic_name),
130 event_name: Cow::Borrowed(event_name),
131 payload,
132 }
133 }
134
135 pub(crate) fn into_err(self) -> Message<()> {
136 Message {
137 join_reference: self.join_reference.map(Cow::into),
138 message_reference: self.message_reference.map(Cow::into),
139 topic_name: self.topic_name.into(),
140 event_name: self.event_name.into(),
141 payload: (),
142 }
143 }
144}
145
146impl<P> Serialize for ChannelMsg<'_, P>
147where
148 P: Serialize,
149{
150 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
151 where
152 S: serde::Serializer,
153 {
154 let Self {
155 join_reference,
156 message_reference,
157 topic_name,
158 event_name,
159 payload,
160 } = self;
161
162 (
163 join_reference,
164 message_reference,
165 topic_name,
166 event_name,
167 payload,
168 )
169 .serialize(serializer)
170 }
171}
172
173impl<'de, 'a, P> Deserialize<'de> for ChannelMsg<'a, P>
174where
175 P: Deserialize<'de>,
176{
177 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
178 where
179 D: serde::Deserializer<'de>,
180 {
181 let (join_reference, message_reference, topic_name, event_name, payload) =
182 Deserialize::deserialize(deserializer)?;
183
184 Ok(Self {
185 join_reference,
186 message_reference,
187 topic_name,
188 event_name,
189 payload,
190 })
191 }
192}
193
194impl<P> Display for ChannelMsg<'_, P>
195where
196 P: Serialize + Debug,
197{
198 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199 let Ok(s) = serde_json::to_string(self) else {
200 return write!(
201 f,
202 "[{:?}, {:?}, {:?}, {:?}, {:?}]",
203 self.join_reference,
204 self.message_reference,
205 self.topic_name,
206 self.event_name,
207 self.payload,
208 );
209 };
210
211 write!(f, "{s}")
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use pretty_assertions::assert_eq;
218
219 use crate::Map;
220
221 use super::*;
222
223 #[test]
224 fn deserialize_payload_to_struct() {
225 let join = Message {
226 join_reference: Some("0".to_string()),
227 message_reference: Some("0".to_string()),
228 topic_name: "phoenix".to_string(),
229 event_name: "phx_reply".to_string(),
230 payload: serde_json::Value::Object(serde_json::Map::from_iter([
231 (
232 "status".to_string(),
233 serde_json::Value::String("ok".to_string()),
234 ),
235 (
236 "response".to_string(),
237 serde_json::Value::Object(serde_json::Map::default()),
238 ),
239 ])),
240 };
241
242 #[derive(Debug, Deserialize, PartialEq, Eq)]
243 struct Payload {
244 status: String,
245 response: Map,
246 }
247
248 let exp = Message {
249 join_reference: join.join_reference.clone(),
250 message_reference: join.message_reference.clone(),
251 topic_name: join.topic_name.clone(),
252 event_name: join.event_name.clone(),
253 payload: Payload {
254 status: "ok".to_string(),
255 response: Map::default(),
256 },
257 };
258
259 let message: Message<Payload> = join.deserialize_payload().unwrap();
260
261 assert_eq!(message, exp);
262 }
263
264 #[test]
265 fn serialize_deserialize_join() {
266 let join = r#"["0","0","miami:weather","phx_join",{"some":"param"}]"#;
267
268 let message: ChannelMsg<Map> = serde_json::from_str(join).unwrap();
269
270 let exp = ChannelMsg::new(
271 Some(0),
272 Some(0),
273 "miami:weather",
274 "phx_join",
275 Map::from_iter([("some".to_string(), "param".to_string())]),
276 );
277
278 assert_eq!(message, exp);
279
280 let json = serde_json::to_string(&message).unwrap();
281
282 assert_eq!(json, join);
283 }
284
285 #[test]
286 fn serialize_deserialize_leave() {
287 let join = r#"[null,"1","miami:weather","phx_leave",{}]"#;
288
289 let message: ChannelMsg<Map> = serde_json::from_str(join).unwrap();
290
291 let exp = ChannelMsg::new(None, Some(1), "miami:weather", "phx_leave", Map::default());
292
293 assert_eq!(message, exp);
294
295 let json = serde_json::to_string(&message).unwrap();
296
297 assert_eq!(json, join);
298 }
299
300 #[test]
301 fn serialize_deserialize_heartbit() {
302 let join = r#"[null,"2","phoenix","heartbeat",{}]"#;
303
304 let message: ChannelMsg<Map> = serde_json::from_str(join).unwrap();
305
306 let exp = ChannelMsg::new(None, Some(2), "phoenix", "heartbeat", Map::default());
307
308 assert_eq!(message, exp);
309
310 let json = serde_json::to_string(&message).unwrap();
311
312 assert_eq!(json, join);
313 }
314
315 #[test]
316 fn serialize_deserialize_send_example() {
317 let join = r#"[null,"3","miami:weather","report_emergency",{"category":"sharknado"}]"#;
318
319 let message: ChannelMsg<Map> = serde_json::from_str(join).unwrap();
320
321 let exp = ChannelMsg::new(
322 None,
323 Some(3),
324 "miami:weather",
325 "report_emergency",
326 Map::from_iter([("category".to_string(), "sharknado".to_string())]),
327 );
328
329 assert_eq!(message, exp);
330
331 let json = serde_json::to_string(&message).unwrap();
332
333 assert_eq!(json, join);
334 }
335
336 #[test]
337 fn serialize_deserialize_no_message_reference() {
338 let join =
339 r#"[null,null,"rooms:test:dashboard_oSgokqqBReiKRg_c1nYqMQ_9899","watch_added",{}]"#;
340
341 let message: ChannelMsg<Map> = serde_json::from_str(join).unwrap();
342
343 let exp = ChannelMsg::new(
344 None,
345 None,
346 "rooms:test:dashboard_oSgokqqBReiKRg_c1nYqMQ_9899",
347 "watch_added",
348 Map::default(),
349 );
350
351 assert_eq!(message, exp);
352
353 let json = serde_json::to_string(&message).unwrap();
354
355 assert_eq!(json, join);
356 }
357}