use self::string_collect::StringCollector;
use super::frame::{CloseFrame, Frame};
use crate::protocol::error::ProtocolError;
use crate::protocol::frame::Utf8Bytes;
use rama_core::bytes::Bytes;
use rama_utils::str::utf8;
use std::{fmt, result::Result as StdResult, str};
mod string_collect {
use rama_core::error::BoxError;
use super::*;
#[derive(Debug)]
pub(super) struct StringCollector {
data: String,
incomplete: Option<utf8::Incomplete>,
}
impl StringCollector {
pub(super) fn new() -> Self {
Self {
data: String::new(),
incomplete: None,
}
}
pub(super) fn len(&self) -> usize {
self.data
.len()
.saturating_add(self.incomplete.map(|i| i.buffer_len as usize).unwrap_or(0))
}
pub(super) fn extend<T: AsRef<[u8]>>(&mut self, tail: T) -> Result<(), ProtocolError> {
let mut input: &[u8] = tail.as_ref();
if let Some(mut incomplete) = self.incomplete.take() {
if let Some((result, rest)) = incomplete.try_complete(input) {
input = rest;
match result {
Ok(text) => self.data.push_str(text),
Err(result_bytes) => {
return Err(ProtocolError::Utf8(BoxError::from(
String::from_utf8_lossy(result_bytes).to_string(),
)));
}
}
} else {
input = &[];
self.incomplete = Some(incomplete);
}
}
if !input.is_empty() {
match utf8::decode(input) {
Ok(text) => {
self.data.push_str(text);
Ok(())
}
Err(utf8::DecodeError::Incomplete {
valid_prefix,
incomplete_suffix,
}) => {
self.data.push_str(valid_prefix);
self.incomplete = Some(incomplete_suffix);
Ok(())
}
Err(utf8::DecodeError::Invalid {
valid_prefix,
invalid_sequence,
..
}) => {
self.data.push_str(valid_prefix);
Err(ProtocolError::Utf8(BoxError::from(
String::from_utf8_lossy(invalid_sequence).to_string(),
)))
}
}
} else {
Ok(())
}
}
pub(super) fn into_string(self) -> Result<String, ProtocolError> {
if let Some(incomplete) = self.incomplete {
Err(ProtocolError::Utf8(BoxError::from(format!(
"incomplete string: {incomplete:?}",
))))
} else {
Ok(self.data)
}
}
}
}
#[derive(Debug)]
pub(super) struct IncompleteMessage {
collector: IncompleteMessageCollector,
}
#[derive(Debug)]
enum IncompleteMessageCollector {
Text(StringCollector),
Binary(Vec<u8>),
}
impl IncompleteMessage {
pub(super) fn new(message_type: IncompleteMessageType) -> Self {
Self {
collector: match message_type {
IncompleteMessageType::Binary => IncompleteMessageCollector::Binary(Vec::new()),
IncompleteMessageType::Text => {
IncompleteMessageCollector::Text(StringCollector::new())
}
},
}
}
pub(super) fn len(&self) -> usize {
match self.collector {
IncompleteMessageCollector::Text(ref t) => t.len(),
IncompleteMessageCollector::Binary(ref b) => b.len(),
}
}
pub(super) fn extend<T: AsRef<[u8]>>(
&mut self,
tail: T,
size_limit: Option<usize>,
) -> Result<(), ProtocolError> {
let max_size = size_limit.unwrap_or_else(usize::max_value);
let my_size = self.len();
let portion_size = tail.as_ref().len();
if my_size > max_size || portion_size > max_size - my_size {
return Err(ProtocolError::MessageTooLong {
size: my_size + portion_size,
max_size,
});
}
match self.collector {
IncompleteMessageCollector::Binary(ref mut v) => {
v.extend(tail.as_ref());
Ok(())
}
IncompleteMessageCollector::Text(ref mut t) => t.extend(tail),
}
}
pub(super) fn complete(self) -> Result<Message, ProtocolError> {
match self.collector {
IncompleteMessageCollector::Binary(v) => Ok(Message::Binary(v.into())),
IncompleteMessageCollector::Text(t) => {
let text = t.into_string()?;
Ok(Message::text(text))
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub(super) enum IncompleteMessageType {
Text,
Binary,
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Message {
Text(Utf8Bytes),
Binary(Bytes),
Ping(Bytes),
Pong(Bytes),
Close(Option<CloseFrame>),
Frame(Frame),
}
impl Message {
pub fn text<S>(string: S) -> Self
where
S: Into<Utf8Bytes>,
{
Self::Text(string.into())
}
pub fn binary<B>(bin: B) -> Self
where
B: Into<Bytes>,
{
Self::Binary(bin.into())
}
pub fn is_text(&self) -> bool {
matches!(*self, Self::Text(_))
}
pub fn is_binary(&self) -> bool {
matches!(*self, Self::Binary(_))
}
pub fn is_ping(&self) -> bool {
matches!(*self, Self::Ping(_))
}
pub fn is_pong(&self) -> bool {
matches!(*self, Self::Pong(_))
}
pub fn is_close(&self) -> bool {
matches!(*self, Self::Close(_))
}
pub fn len(&self) -> usize {
match *self {
Self::Text(ref string) => string.len(),
Self::Binary(ref data) | Self::Ping(ref data) | Self::Pong(ref data) => data.len(),
Self::Close(ref data) => data.as_ref().map(|d| d.reason.len()).unwrap_or(0),
Self::Frame(ref frame) => frame.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn into_data(self) -> Bytes {
match self {
Self::Text(utf8) => utf8.into(),
Self::Binary(data) | Self::Ping(data) | Self::Pong(data) => data,
Self::Close(None) => <_>::default(),
Self::Close(Some(frame)) => frame.reason.into(),
Self::Frame(frame) => frame.into_payload(),
}
}
pub fn into_text(self) -> Result<Utf8Bytes, ProtocolError> {
match self {
Self::Text(txt) => Ok(txt),
Self::Binary(data) | Self::Ping(data) | Self::Pong(data) => Ok(data.try_into()?),
Self::Close(None) => Ok(<_>::default()),
Self::Close(Some(frame)) => Ok(frame.reason),
Self::Frame(frame) => Ok(frame.into_text()?),
}
}
pub fn to_text(&self) -> Result<&str, ProtocolError> {
match *self {
Self::Text(ref string) => Ok(string.as_str()),
Self::Binary(ref data) | Self::Ping(ref data) | Self::Pong(ref data) => {
Ok(str::from_utf8(data)?)
}
Self::Close(None) => Ok(""),
Self::Close(Some(ref frame)) => Ok(&frame.reason),
Self::Frame(ref frame) => Ok(frame.to_text()?),
}
}
}
impl From<String> for Message {
#[inline]
fn from(string: String) -> Self {
Self::text(string)
}
}
impl<'s> From<&'s str> for Message {
#[inline]
fn from(string: &'s str) -> Self {
Self::text(string)
}
}
impl<'b> From<&'b [u8]> for Message {
#[inline]
fn from(data: &'b [u8]) -> Self {
Self::binary(Bytes::copy_from_slice(data))
}
}
impl From<Bytes> for Message {
fn from(data: Bytes) -> Self {
Self::binary(data)
}
}
impl From<Vec<u8>> for Message {
#[inline]
fn from(data: Vec<u8>) -> Self {
Self::binary(data)
}
}
impl From<Message> for Bytes {
#[inline]
fn from(message: Message) -> Self {
message.into_data()
}
}
impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter) -> StdResult<(), fmt::Error> {
match self {
Self::Text(utf8_bytes) => write!(f, "Message::Text({utf8_bytes})"),
Self::Binary(bytes) => write!(f, "Message::Binary({bytes:x})"),
Self::Ping(bytes) => write!(f, "Message::Ping({bytes:x})"),
Self::Pong(bytes) => write!(f, "Message::Pong({bytes:x})"),
Self::Close(_) => write!(f, "Message::Close<length={}>", self.len()),
Self::Frame(_) => write!(f, "Message::Frame<length={}>", self.len()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display() {
let t = Message::text("test".to_owned());
assert_eq!(t.to_string(), "Message::Text(test)".to_owned());
let bin = Message::binary(vec![0, 1, 3, 4, 241]);
assert_eq!(bin.to_string(), "Message::Binary(00010304f1)".to_owned());
}
#[test]
fn binary_convert() {
let bin = [6u8, 7, 8, 9, 10, 241];
let msg = Message::from(&bin[..]);
assert!(msg.is_binary());
msg.into_text().unwrap_err();
}
#[test]
fn binary_convert_bytes() {
let bin = Bytes::from_iter([6u8, 7, 8, 9, 10, 241]);
let msg = Message::from(bin);
assert!(msg.is_binary());
msg.into_text().unwrap_err();
}
#[test]
fn binary_convert_vec() {
let bin = vec![6u8, 7, 8, 9, 10, 241];
let msg = Message::from(bin);
assert!(msg.is_binary());
msg.into_text().unwrap_err();
}
#[test]
fn binary_convert_into_bytes() {
let bin = vec![6u8, 7, 8, 9, 10, 241];
let bin_copy = bin.clone();
let msg = Message::from(bin);
let serialized: Bytes = msg.into();
assert_eq!(bin_copy, serialized);
}
#[test]
fn text_convert() {
let s = "kiwotsukete";
let msg = Message::from(s);
assert!(msg.is_text());
}
}