use bytes::Bytes;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MessageType {
Text,
Binary,
Ping,
Pong,
Close,
}
#[derive(Debug, Clone)]
pub struct Message {
pub message_type: MessageType,
pub payload: Bytes,
}
impl Message {
pub fn text<S: Into<String>>(text: S) -> Self {
Self {
message_type: MessageType::Text,
payload: Bytes::from(text.into()),
}
}
pub fn binary<B: Into<Bytes>>(data: B) -> Self {
Self {
message_type: MessageType::Binary,
payload: data.into(),
}
}
pub fn ping<B: Into<Bytes>>(data: B) -> Self {
Self {
message_type: MessageType::Ping,
payload: data.into(),
}
}
pub fn pong<B: Into<Bytes>>(data: B) -> Self {
Self {
message_type: MessageType::Pong,
payload: data.into(),
}
}
pub fn close() -> Self {
Self {
message_type: MessageType::Close,
payload: Bytes::new(),
}
}
pub fn json<T: Serialize>(value: &T) -> Result<Self, serde_json::Error> {
let json = serde_json::to_string(value)?;
Ok(Self::text(json))
}
pub fn parse_json<'a, T: Deserialize<'a>>(&'a self) -> Result<T, serde_json::Error> {
serde_json::from_slice(&self.payload)
}
pub fn as_text(&self) -> Option<&str> {
if self.message_type == MessageType::Text {
std::str::from_utf8(&self.payload).ok()
} else {
None
}
}
pub fn as_bytes(&self) -> &[u8] {
&self.payload
}
pub fn is_text(&self) -> bool {
self.message_type == MessageType::Text
}
pub fn is_binary(&self) -> bool {
self.message_type == MessageType::Binary
}
pub fn is_ping(&self) -> bool {
self.message_type == MessageType::Ping
}
pub fn is_pong(&self) -> bool {
self.message_type == MessageType::Pong
}
pub fn is_close(&self) -> bool {
self.message_type == MessageType::Close
}
}
impl From<tungstenite::Message> for Message {
fn from(msg: tungstenite::Message) -> Self {
match msg {
tungstenite::Message::Text(text) => Self::text(text.to_string()),
tungstenite::Message::Binary(data) => Self::binary(Bytes::from(data)),
tungstenite::Message::Ping(data) => Self::ping(Bytes::from(data)),
tungstenite::Message::Pong(data) => Self::pong(Bytes::from(data)),
tungstenite::Message::Close(_) => Self::close(),
tungstenite::Message::Frame(_) => Self::binary(Bytes::new()),
}
}
}
impl From<Message> for tungstenite::Message {
fn from(msg: Message) -> Self {
match msg.message_type {
MessageType::Text => {
tungstenite::Message::Text(String::from_utf8_lossy(&msg.payload).into_owned().into())
}
MessageType::Binary => tungstenite::Message::Binary(msg.payload.to_vec().into()),
MessageType::Ping => tungstenite::Message::Ping(msg.payload.to_vec().into()),
MessageType::Pong => tungstenite::Message::Pong(msg.payload.to_vec().into()),
MessageType::Close => tungstenite::Message::Close(None),
}
}
}