use std::{fmt, str};
use crate::Error;
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Message {
Text(String),
Binary(Vec<u8>),
Ping(Vec<u8>),
Pong(Vec<u8>),
#[cfg(feature = "tokio-ws")]
Close(Option<async_tungstenite::tungstenite::protocol::CloseFrame<'static>>),
#[cfg(feature = "tokio-ws")]
Frame(async_tungstenite::tungstenite::protocol::frame::Frame),
}
impl Message {
pub fn text<S>(string: S) -> Message
where
S: Into<String>,
{
Message::Text(string.into())
}
pub fn binary<B>(bin: B) -> Message
where
B: Into<Vec<u8>>,
{
Message::Binary(bin.into())
}
pub fn is_text(&self) -> bool {
matches!(*self, Message::Text(_))
}
pub fn is_binary(&self) -> bool {
matches!(*self, Message::Binary(_))
}
pub fn is_ping(&self) -> bool {
matches!(*self, Message::Ping(_))
}
pub fn is_pong(&self) -> bool {
matches!(*self, Message::Pong(_))
}
#[cfg(feature = "tokio-ws")]
pub fn is_close(&self) -> bool {
matches!(*self, Message::Close(_))
}
pub fn len(&self) -> usize {
match *self {
Message::Text(ref string) => string.len(),
Message::Binary(ref data) | Message::Ping(ref data) | Message::Pong(ref data) => {
data.len()
}
#[cfg(feature = "tokio-ws")]
Message::Close(ref data) => data.as_ref().map(|d| d.reason.len()).unwrap_or(0),
#[cfg(feature = "tokio-ws")]
Message::Frame(ref frame) => frame.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn into_data(self) -> Vec<u8> {
match self {
Message::Text(string) => string.into_bytes(),
Message::Binary(data) | Message::Ping(data) | Message::Pong(data) => data,
#[cfg(feature = "tokio-ws")]
Message::Close(None) => Vec::new(),
#[cfg(feature = "tokio-ws")]
Message::Close(Some(frame)) => frame.reason.into_owned().into_bytes(),
#[cfg(feature = "tokio-ws")]
Message::Frame(frame) => frame.into_data(),
}
}
pub fn into_text(self) -> Result<String, Error> {
match self {
Message::Text(string) => Ok(string),
Message::Binary(data) | Message::Ping(data) | Message::Pong(data) => {
Ok(String::from_utf8(data).map_err(|_| Error::Utf8)?)
}
#[cfg(feature = "tokio-ws")]
Message::Close(None) => Ok(String::new()),
#[cfg(feature = "tokio-ws")]
Message::Close(Some(frame)) => Ok(frame.reason.into_owned()),
#[cfg(feature = "tokio-ws")]
Message::Frame(frame) => Ok(frame.into_string().map_err(|_| Error::Utf8)?),
}
}
pub fn to_text(&self) -> Result<&str, Error> {
match *self {
Message::Text(ref string) => Ok(string),
Message::Binary(ref data) | Message::Ping(ref data) | Message::Pong(ref data) => {
Ok(str::from_utf8(data).map_err(|_| Error::Utf8)?)
}
#[cfg(feature = "tokio-ws")]
Message::Close(None) => Ok(""),
#[cfg(feature = "tokio-ws")]
Message::Close(Some(ref frame)) => Ok(&frame.reason),
#[cfg(feature = "tokio-ws")]
Message::Frame(ref frame) => Ok(frame.to_text().map_err(|_| Error::Utf8)?),
}
}
}
impl From<String> for Message {
fn from(string: String) -> Self {
Message::text(string)
}
}
impl<'s> From<&'s str> for Message {
fn from(string: &'s str) -> Self {
Message::text(string)
}
}
impl<'b> From<&'b [u8]> for Message {
fn from(data: &'b [u8]) -> Self {
Message::binary(data)
}
}
impl From<Vec<u8>> for Message {
fn from(data: Vec<u8>) -> Self {
Message::binary(data)
}
}
impl From<Message> for Vec<u8> {
fn from(message: Message) -> Self {
message.into_data()
}
}
impl TryFrom<Message> for String {
type Error = Error;
fn try_from(value: Message) -> Result<Self, Error> {
value.into_text()
}
}
impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
if let Ok(string) = self.to_text() {
write!(f, "{}", string)
} else {
write!(f, "Binary Data<length={}>", self.len())
}
}
}
#[cfg(feature = "tokio-ws")]
impl From<async_tungstenite::tungstenite::Message> for Message {
fn from(msg: async_tungstenite::tungstenite::Message) -> Self {
match msg {
async_tungstenite::tungstenite::Message::Text(string) => Message::Text(string),
async_tungstenite::tungstenite::Message::Binary(data) => Message::Binary(data),
async_tungstenite::tungstenite::Message::Ping(data) => Message::Ping(data),
async_tungstenite::tungstenite::Message::Pong(data) => Message::Pong(data),
#[cfg(feature = "tokio-ws")]
async_tungstenite::tungstenite::Message::Close(frame) => Message::Close(frame),
#[cfg(not(feature = "tokio-ws"))]
async_tungstenite::tungstenite::Message::Close(frame) => unreachable!(),
#[cfg(feature = "tokio-ws")]
async_tungstenite::tungstenite::Message::Frame(frame) => Message::Frame(frame),
#[cfg(not(feature = "tokio-ws"))]
async_tungstenite::tungstenite::Message::Frame(frame) => unreachable!(),
}
}
}
#[cfg(feature = "tokio-ws")]
impl From<Message> for async_tungstenite::tungstenite::Message {
fn from(v: Message) -> Self {
match v {
Message::Text(string) => Self::Text(string),
Message::Binary(data) => Self::Binary(data),
Message::Ping(data) => Self::Ping(data),
Message::Pong(data) => Self::Pong(data),
#[cfg(feature = "tokio-ws")]
Message::Close(frame) => Self::Close(frame),
#[cfg(feature = "tokio-ws")]
Message::Frame(frame) => Self::Frame(frame),
}
}
}