Skip to main content

doido_cable/
protocol.rs

1use serde::{Deserialize, Serialize};
2
3// Client→server frames are tagged with `command` per the ActionCable wire
4// protocol (subscribe/unsubscribe/message); `type` is reserved for the
5// server→client frames modelled by `ServerFrame`/`ServerMessage`.
6
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
8#[serde(tag = "command", rename_all = "lowercase")]
9pub enum CableFrame {
10    Subscribe {
11        identifier: String,
12    },
13    Unsubscribe {
14        identifier: String,
15    },
16    Message {
17        identifier: String,
18        data: serde_json::Value,
19    },
20}
21
22impl CableFrame {
23    pub fn parse(json: &str) -> doido_core::Result<Self> {
24        serde_json::from_str(json)
25            .map_err(|e| doido_core::anyhow::anyhow!("invalid cable frame: {e}"))
26    }
27
28    pub fn to_json(&self) -> doido_core::Result<String> {
29        serde_json::to_string(self)
30            .map_err(|e| doido_core::anyhow::anyhow!("cable frame serialize error: {e}"))
31    }
32}
33
34/// Server→client control frames — the ActionCable `type`-tagged messages
35/// (`welcome`, `ping`, `confirm_subscription`, `reject_subscription`).
36///
37/// The broadcast frame that carries channel data has no `type` tag and is
38/// modelled separately as [`ServerMessage`].
39#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
40#[serde(tag = "type", rename_all = "snake_case")]
41pub enum ServerFrame {
42    Welcome,
43    Ping { message: i64 },
44    ConfirmSubscription { identifier: String },
45    RejectSubscription { identifier: String },
46}
47
48impl ServerFrame {
49    pub fn parse(json: &str) -> doido_core::Result<Self> {
50        serde_json::from_str(json)
51            .map_err(|e| doido_core::anyhow::anyhow!("invalid server frame: {e}"))
52    }
53
54    pub fn to_json(&self) -> doido_core::Result<String> {
55        serde_json::to_string(self)
56            .map_err(|e| doido_core::anyhow::anyhow!("server frame serialize error: {e}"))
57    }
58}
59
60/// Server→client broadcast frame: channel data delivered to subscribers. Per the
61/// ActionCable wire protocol it carries no `type` tag —
62/// `{ "identifier": "...", "message": {...} }`.
63#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
64pub struct ServerMessage {
65    pub identifier: String,
66    pub message: serde_json::Value,
67}
68
69impl ServerMessage {
70    pub fn new(identifier: impl Into<String>, message: serde_json::Value) -> Self {
71        Self {
72            identifier: identifier.into(),
73            message,
74        }
75    }
76
77    pub fn parse(json: &str) -> doido_core::Result<Self> {
78        serde_json::from_str(json)
79            .map_err(|e| doido_core::anyhow::anyhow!("invalid server message: {e}"))
80    }
81
82    pub fn to_json(&self) -> doido_core::Result<String> {
83        serde_json::to_string(self)
84            .map_err(|e| doido_core::anyhow::anyhow!("server message serialize error: {e}"))
85    }
86}