use super::Encode;
use bytes::{Buf, BufMut, Bytes, BytesMut};
pub type Data = Bytes;
pub type Metadata = Bytes;
#[derive(Clone, Default, Eq, PartialEq, Debug)]
pub struct Payload {
pub(crate) metadata: Option<Metadata>,
pub(crate) data: Option<Data>,
}
impl Payload {
pub(crate) fn new(metadata: Option<Metadata>, data: Option<Data>) -> Self {
Payload { metadata, data }
}
pub fn builder() -> PayloadBuilder {
PayloadBuilder::new()
}
pub fn len(&self) -> usize {
let mut len = 0;
if let Some(metadata) = &self.metadata {
len += metadata.len();
}
if let Some(data) = &self.data {
len += data.len();
}
len
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn data(&self) -> Option<&Data> {
self.data.as_ref()
}
pub fn metadata(&self) -> Option<&Metadata> {
self.metadata.as_ref()
}
pub fn data_utf8(&self) -> Option<&str> {
if let Some(ref data) = self.data {
std::str::from_utf8(data).ok()
} else {
None
}
}
pub fn metadata_utf8(&self) -> Option<&str> {
if let Some(ref metadata) = self.metadata {
std::str::from_utf8(metadata).ok()
} else {
None
}
}
pub fn has_data(&self) -> bool {
self.data.is_some()
}
pub fn has_metadata(&self) -> bool {
self.metadata.is_some()
}
pub fn split(self) -> (Option<Metadata>, Option<Data>) {
(self.metadata, self.data)
}
pub fn chunks(self, mtu: usize) -> PayloadChunks {
let (metadata, data) = self.split();
PayloadChunks { mtu, metadata, data }
}
}
#[derive(Debug)]
pub struct PayloadBuilder(Payload);
impl PayloadBuilder {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
let payload = Payload::default();
PayloadBuilder(payload)
}
pub fn set_data<T>(mut self, data: T) -> Self
where
T: Into<Bytes>,
{
self.0.data = Some(data.into());
self
}
pub fn set_metadata<T>(mut self, metadata: T) -> Self
where
T: Into<Bytes>,
{
self.0.metadata = Some(metadata.into());
self
}
pub fn build(self) -> Payload {
self.0
}
}
impl Encode for Payload {
fn encode(&self, buf: &mut BytesMut) {
if let Some(meta) = &self.metadata {
buf.put_slice(meta);
}
if let Some(data) = &self.data {
buf.put_slice(data);
}
}
fn len(&self) -> usize {
let mut len = 0;
if let Some(metadata) = &self.metadata {
len += metadata.len();
}
if let Some(data) = &self.data {
len += data.len();
}
len
}
}
#[derive(Debug)]
pub struct PayloadChunks {
mtu: usize,
metadata: Option<Bytes>,
data: Option<Bytes>,
}
impl Iterator for PayloadChunks {
type Item = Payload;
fn next(&mut self) -> Option<Self::Item> {
if self.metadata.is_none() && self.data.is_none() {
return None;
}
let mut meta = None;
let mut data = None;
if let Some(m) = &mut self.metadata {
let len = m.remaining();
if self.mtu < len {
meta = Some(m.split_to(self.mtu));
} else {
meta = self.metadata.take();
}
}
if let Some(d) = &mut self.data {
let len = d.remaining();
if self.mtu < len {
data = Some(d.split_to(self.mtu));
} else {
data = self.data.take();
}
}
Some(Payload::new(meta, data))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_chunks() {
let payload = Payload::builder()
.set_metadata("metadata")
.set_data("data payload")
.build();
let mut iter = payload.chunks(4);
assert_eq!(
iter.next(),
Some(
Payload::builder()
.set_metadata("meta")
.set_data("data")
.build()
)
);
assert_eq!(
iter.next(),
Some(
Payload::builder()
.set_metadata("data")
.set_data(" pay")
.build()
)
);
assert_eq!(
iter.next(),
Some(Payload::builder().set_data("load").build())
);
assert_eq!(iter.next(), None);
assert_eq!(iter.next(), None);
}
}