1use serde::{de::DeserializeOwned, Serialize};
4use std::borrow::Cow;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum Message {
9 Text(String),
11 Binary(Vec<u8>),
13 Ping(Vec<u8>),
15 Pong(Vec<u8>),
17 Close(Option<CloseFrame>),
19}
20
21impl Message {
22 pub fn text(text: impl Into<String>) -> Self {
24 Self::Text(text.into())
25 }
26
27 pub fn binary(data: impl Into<Vec<u8>>) -> Self {
29 Self::Binary(data.into())
30 }
31
32 pub fn ping(data: impl Into<Vec<u8>>) -> Self {
34 Self::Ping(data.into())
35 }
36
37 pub fn pong(data: impl Into<Vec<u8>>) -> Self {
39 Self::Pong(data.into())
40 }
41
42 pub fn close() -> Self {
44 Self::Close(None)
45 }
46
47 pub fn close_with(code: CloseCode, reason: impl Into<String>) -> Self {
49 Self::Close(Some(CloseFrame {
50 code,
51 reason: Cow::Owned(reason.into()),
52 }))
53 }
54
55 pub fn json<T: Serialize>(value: &T) -> Result<Self, crate::WebSocketError> {
57 serde_json::to_string(value)
58 .map(Self::Text)
59 .map_err(|e| crate::WebSocketError::serialization_error(e.to_string()))
60 }
61
62 pub fn as_json<T: DeserializeOwned>(&self) -> Result<T, crate::WebSocketError> {
64 match self {
65 Self::Text(text) => serde_json::from_str(text)
66 .map_err(|e| crate::WebSocketError::deserialization_error(e.to_string())),
67 _ => Err(crate::WebSocketError::deserialization_error(
68 "Expected text message for JSON deserialization",
69 )),
70 }
71 }
72
73 pub fn is_text(&self) -> bool {
75 matches!(self, Self::Text(_))
76 }
77
78 pub fn is_binary(&self) -> bool {
80 matches!(self, Self::Binary(_))
81 }
82
83 pub fn is_ping(&self) -> bool {
85 matches!(self, Self::Ping(_))
86 }
87
88 pub fn is_pong(&self) -> bool {
90 matches!(self, Self::Pong(_))
91 }
92
93 pub fn is_close(&self) -> bool {
95 matches!(self, Self::Close(_))
96 }
97
98 pub fn as_text(&self) -> Option<&str> {
100 match self {
101 Self::Text(text) => Some(text),
102 _ => None,
103 }
104 }
105
106 pub fn as_bytes(&self) -> Option<&[u8]> {
108 match self {
109 Self::Binary(data) => Some(data),
110 _ => None,
111 }
112 }
113
114 pub fn into_text(self) -> Option<String> {
116 match self {
117 Self::Text(text) => Some(text),
118 _ => None,
119 }
120 }
121
122 pub fn into_bytes(self) -> Option<Vec<u8>> {
124 match self {
125 Self::Binary(data) => Some(data),
126 _ => None,
127 }
128 }
129}
130
131impl From<String> for Message {
132 fn from(text: String) -> Self {
133 Self::Text(text)
134 }
135}
136
137impl From<&str> for Message {
138 fn from(text: &str) -> Self {
139 Self::Text(text.to_string())
140 }
141}
142
143impl From<Vec<u8>> for Message {
144 fn from(data: Vec<u8>) -> Self {
145 Self::Binary(data)
146 }
147}
148
149impl From<&[u8]> for Message {
150 fn from(data: &[u8]) -> Self {
151 Self::Binary(data.to_vec())
152 }
153}
154
155impl From<tungstenite::Message> for Message {
157 fn from(msg: tungstenite::Message) -> Self {
158 match msg {
159 tungstenite::Message::Text(text) => Self::Text(text.to_string()),
160 tungstenite::Message::Binary(data) => Self::Binary(data.to_vec()),
161 tungstenite::Message::Ping(data) => Self::Ping(data.to_vec()),
162 tungstenite::Message::Pong(data) => Self::Pong(data.to_vec()),
163 tungstenite::Message::Close(frame) => Self::Close(frame.map(|f| CloseFrame {
164 code: CloseCode::from(f.code),
165 reason: Cow::Owned(f.reason.to_string()),
166 })),
167 tungstenite::Message::Frame(_) => Self::Binary(vec![]), }
169 }
170}
171
172impl From<Message> for tungstenite::Message {
174 fn from(msg: Message) -> Self {
175 match msg {
176 Message::Text(text) => tungstenite::Message::Text(text),
177 Message::Binary(data) => tungstenite::Message::Binary(data),
178 Message::Ping(data) => tungstenite::Message::Ping(data),
179 Message::Pong(data) => tungstenite::Message::Pong(data),
180 Message::Close(frame) => {
181 tungstenite::Message::Close(frame.map(|f| tungstenite::protocol::CloseFrame {
182 code: f.code.into(),
183 reason: f.reason,
184 }))
185 }
186 }
187 }
188}
189
190#[derive(Debug, Clone, PartialEq, Eq)]
192pub struct CloseFrame {
193 pub code: CloseCode,
195 pub reason: Cow<'static, str>,
197}
198
199impl CloseFrame {
200 pub fn new(code: CloseCode, reason: impl Into<Cow<'static, str>>) -> Self {
202 Self {
203 code,
204 reason: reason.into(),
205 }
206 }
207
208 pub fn normal() -> Self {
210 Self::new(CloseCode::Normal, "")
211 }
212
213 pub fn going_away() -> Self {
215 Self::new(CloseCode::Away, "Going away")
216 }
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
221pub enum CloseCode {
222 Normal,
224 Away,
226 Protocol,
228 Unsupported,
230 Status,
232 Abnormal,
234 Invalid,
236 Policy,
238 Size,
240 Extension,
242 Error,
244 Restart,
246 Again,
248 Tls,
250 Reserved(u16),
252 Library(u16),
254 Private(u16),
256}
257
258impl CloseCode {
259 pub fn as_u16(&self) -> u16 {
261 match self {
262 Self::Normal => 1000,
263 Self::Away => 1001,
264 Self::Protocol => 1002,
265 Self::Unsupported => 1003,
266 Self::Status => 1005,
267 Self::Abnormal => 1006,
268 Self::Invalid => 1007,
269 Self::Policy => 1008,
270 Self::Size => 1009,
271 Self::Extension => 1010,
272 Self::Error => 1011,
273 Self::Restart => 1012,
274 Self::Again => 1013,
275 Self::Tls => 1015,
276 Self::Reserved(code) => *code,
277 Self::Library(code) => *code,
278 Self::Private(code) => *code,
279 }
280 }
281}
282
283impl From<u16> for CloseCode {
284 fn from(code: u16) -> Self {
285 match code {
286 1000 => Self::Normal,
287 1001 => Self::Away,
288 1002 => Self::Protocol,
289 1003 => Self::Unsupported,
290 1005 => Self::Status,
291 1006 => Self::Abnormal,
292 1007 => Self::Invalid,
293 1008 => Self::Policy,
294 1009 => Self::Size,
295 1010 => Self::Extension,
296 1011 => Self::Error,
297 1012 => Self::Restart,
298 1013 => Self::Again,
299 1015 => Self::Tls,
300 1004 | 1014 | 1016..=2999 => Self::Reserved(code),
301 3000..=3999 => Self::Library(code),
302 4000..=4999 => Self::Private(code),
303 _ => Self::Reserved(code),
304 }
305 }
306}
307
308impl From<CloseCode> for u16 {
309 fn from(code: CloseCode) -> Self {
310 code.as_u16()
311 }
312}
313
314impl From<tungstenite::protocol::frame::coding::CloseCode> for CloseCode {
315 fn from(code: tungstenite::protocol::frame::coding::CloseCode) -> Self {
316 Self::from(u16::from(code))
317 }
318}
319
320impl From<CloseCode> for tungstenite::protocol::frame::coding::CloseCode {
321 fn from(code: CloseCode) -> Self {
322 tungstenite::protocol::frame::coding::CloseCode::from(code.as_u16())
323 }
324}