routerify_websocket/
message.rs

1use crate::CloseCode;
2#[cfg(feature = "json")]
3use serde::{de::DeserializeOwned, ser::Serialize};
4use std::borrow::Cow;
5use std::fmt;
6use tokio_tungstenite::tungstenite::protocol::{self, CloseFrame};
7
8/// A WebSocket message.
9#[derive(Eq, PartialEq, Clone)]
10pub struct Message {
11    pub(crate) inner: protocol::Message,
12}
13
14impl Message {
15    /// Create a new `Text` WebSocket message from a stringable.
16    pub fn text<S: Into<String>>(s: S) -> Message {
17        Message {
18            inner: protocol::Message::text(s),
19        }
20    }
21
22    /// Constructs a `Text` WebSocket message with the json value.
23    ///
24    /// # Optional
25    ///
26    /// This requires the optional `json` feature to be enabled.
27    #[cfg(feature = "json")]
28    pub fn json<T: Serialize>(value: &T) -> crate::Result<Message> {
29        Ok(Message::text(
30            serde_json::to_string(&value).map_err(|err| crate::WebsocketError::EncodeJson(err.into()))?,
31        ))
32    }
33
34    /// Create a new `Binary` WebSocket message.
35    pub fn binary<V: Into<Vec<u8>>>(v: V) -> Message {
36        Message {
37            inner: protocol::Message::binary(v),
38        }
39    }
40
41    /// Construct a new `Ping` WebSocket message.
42    ///
43    /// The payload here must have a length less than 125 bytes.
44    pub fn ping<V: Into<Vec<u8>>>(v: V) -> Message {
45        Message {
46            inner: protocol::Message::Ping(v.into()),
47        }
48    }
49
50    /// Construct a new `Pong` WebSocket message.
51    ///
52    /// The payload here must have a length less than 125 bytes.
53    pub fn pong<V: Into<Vec<u8>>>(v: V) -> Message {
54        Message {
55            inner: protocol::Message::Pong(v.into()),
56        }
57    }
58
59    /// Construct the default `Close` WebSocket message.
60    pub fn close() -> Message {
61        Message {
62            inner: protocol::Message::Close(None),
63        }
64    }
65
66    /// Construct a `Close` WebSocket message with a code and reason.
67    pub fn close_with<R: Into<Cow<'static, str>>>(code: CloseCode, reason: R) -> Message {
68        Message {
69            inner: protocol::Message::Close(Some(CloseFrame {
70                code,
71                reason: reason.into(),
72            })),
73        }
74    }
75
76    /// Returns true if this message is a `Text` message.
77    pub fn is_text(&self) -> bool {
78        self.inner.is_text()
79    }
80
81    /// Returns true if this message is a `Binary` message.
82    pub fn is_binary(&self) -> bool {
83        self.inner.is_binary()
84    }
85
86    /// Returns true if this message a is a `Close` message.
87    pub fn is_close(&self) -> bool {
88        self.inner.is_close()
89    }
90
91    /// Returns true if this message is a `Ping` message.
92    pub fn is_ping(&self) -> bool {
93        self.inner.is_ping()
94    }
95
96    /// Returns true if this message is a `Pong` message.
97    pub fn is_pong(&self) -> bool {
98        self.inner.is_pong()
99    }
100
101    /// Get the length of the WebSocket message.
102    pub fn len(&self) -> usize {
103        self.inner.len()
104    }
105
106    /// Returns true if the WebSocket message has no content.
107    /// For example, if the other side of the connection sent an empty string.
108    pub fn is_empty(&self) -> bool {
109        self.inner.is_empty()
110    }
111
112    /// The `Close` code if available.
113    pub fn close_code(&self) -> Option<CloseCode> {
114        match self.inner {
115            protocol::Message::Close(Some(ref data)) => Some(data.code),
116            _ => None,
117        }
118    }
119
120    /// The `Close` reason if available.
121    pub fn close_reason(&self) -> Option<&str> {
122        match self.inner {
123            protocol::Message::Close(Some(ref data)) => Some(&data.reason),
124            _ => None,
125        }
126    }
127
128    /// Attempts to convert the message data as text in `UTF8` format.
129    pub fn as_text(&self) -> crate::Result<&str> {
130        self.inner
131            .to_text()
132            .map_err(|err| crate::WebsocketError::DecodeText(err.into()))
133    }
134
135    /// Return the bytes of this message.
136    pub fn as_bytes(&self) -> &[u8] {
137        match self.inner {
138            protocol::Message::Text(ref s) => s.as_bytes(),
139            protocol::Message::Binary(ref v) => v,
140            protocol::Message::Ping(ref v) => v,
141            protocol::Message::Pong(ref v) => v,
142            protocol::Message::Close(_) => &[],
143        }
144    }
145
146    /// Consumes the message and returns its data as bytes.
147    pub fn into_bytes(self) -> Vec<u8> {
148        self.inner.into_data()
149    }
150
151    /// Consumes the WebSocket message and attempts to converts it to a `String`.
152    pub fn into_text(self) -> crate::Result<String> {
153        self.inner
154            .into_text()
155            .map_err(|err| crate::WebsocketError::DecodeText(err.into()))
156    }
157
158    /// Try to deserialize the message data as `JSON`.
159    ///
160    /// # Optional
161    ///
162    /// This requires the optional `json` feature to be enabled.
163    #[cfg(feature = "json")]
164    pub fn decode_json<T: DeserializeOwned>(self) -> crate::Result<T> {
165        serde_json::from_slice(&self.into_bytes()).map_err(|err| crate::WebsocketError::DecodeJson(err.into()))
166    }
167}
168
169impl fmt::Debug for Message {
170    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171        fmt::Debug::fmt(&self.inner, f)
172    }
173}
174
175impl Into<Vec<u8>> for Message {
176    fn into(self) -> Vec<u8> {
177        self.into_bytes()
178    }
179}