Skip to main content

ws_bridge/
codec.rs

1use std::fmt;
2
3/// A WebSocket message — either text or binary.
4///
5/// This is the library's own message type, independent of any specific
6/// WebSocket implementation. Feature-gated modules convert to/from
7/// their native message types internally.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum WsMessage {
10    Text(String),
11    Binary(Vec<u8>),
12}
13
14/// Error encoding a message into a WebSocket frame.
15#[derive(Debug)]
16pub enum EncodeError {
17    Json(serde_json::Error),
18    Custom(String),
19}
20
21impl fmt::Display for EncodeError {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        match self {
24            EncodeError::Json(e) => write!(f, "JSON encode error: {e}"),
25            EncodeError::Custom(msg) => write!(f, "encode error: {msg}"),
26        }
27    }
28}
29
30impl std::error::Error for EncodeError {}
31
32impl From<serde_json::Error> for EncodeError {
33    fn from(e: serde_json::Error) -> Self {
34        EncodeError::Json(e)
35    }
36}
37
38/// Error decoding a WebSocket frame into a typed message.
39#[derive(Debug)]
40pub enum DecodeError {
41    Json(serde_json::Error),
42    UnexpectedBinary,
43    UnexpectedText,
44    InvalidData(String),
45}
46
47impl fmt::Display for DecodeError {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        match self {
50            DecodeError::Json(e) => write!(f, "JSON decode error: {e}"),
51            DecodeError::UnexpectedBinary => write!(f, "expected text frame, got binary"),
52            DecodeError::UnexpectedText => write!(f, "expected binary frame, got text"),
53            DecodeError::InvalidData(msg) => write!(f, "invalid data: {msg}"),
54        }
55    }
56}
57
58impl std::error::Error for DecodeError {}
59
60impl From<serde_json::Error> for DecodeError {
61    fn from(e: serde_json::Error) -> Self {
62        DecodeError::Json(e)
63    }
64}
65
66/// Encode/decode messages to/from WebSocket frames.
67///
68/// A blanket implementation covers anything that implements
69/// `Serialize + DeserializeOwned`, encoding as JSON text frames.
70/// Implement manually for binary protocols.
71pub trait WsCodec: Sized {
72    fn encode(&self) -> Result<WsMessage, EncodeError>;
73    fn decode(msg: WsMessage) -> Result<Self, DecodeError>;
74}
75
76/// Blanket impl: JSON text encoding for any serde type.
77impl<T: serde::Serialize + serde::de::DeserializeOwned> WsCodec for T {
78    fn encode(&self) -> Result<WsMessage, EncodeError> {
79        Ok(WsMessage::Text(serde_json::to_string(self)?))
80    }
81
82    fn decode(msg: WsMessage) -> Result<Self, DecodeError> {
83        match msg {
84            WsMessage::Text(text) => Ok(serde_json::from_str(&text)?),
85            WsMessage::Binary(_) => Err(DecodeError::UnexpectedBinary),
86        }
87    }
88}
89
90/// A type representing no messages. Use this as `ClientMsg` or `ServerMsg`
91/// for endpoints that are unidirectional (e.g., server-push-only streams).
92///
93/// Does not implement `Serialize`/`DeserializeOwned`, so it won't conflict
94/// with the blanket JSON impl. Encoding always fails; decoding always fails.
95pub enum NoMessages {}
96
97impl Clone for NoMessages {
98    fn clone(&self) -> Self {
99        match *self {}
100    }
101}
102
103impl WsCodec for NoMessages {
104    fn encode(&self) -> Result<WsMessage, EncodeError> {
105        match *self {}
106    }
107
108    fn decode(_msg: WsMessage) -> Result<Self, DecodeError> {
109        Err(DecodeError::InvalidData(
110            "this endpoint does not accept messages in this direction".into(),
111        ))
112    }
113}