use core::{
convert::Infallible,
fmt::Debug,
ops::{
ControlFlow,
FromResidual,
Try,
},
};
use crate::Success;
pub trait Streaming: Sized + Clone + Eq + Debug {
type Item: Debug;
type Error: Debug;
type Span: Debug + Streaming;
fn split_first(self) -> Split<Self::Item, Self, Self::Error>;
fn split_at(self, mid: usize) -> Split<Self::Span, Self, Self::Error>;
fn split_last(self) -> Split<Self::Item, Self, Self::Error>;
fn all(self) -> Result<Success<Self::Span, Self>, Self::Error>;
fn diff(self, other: &Self) -> Result<Self::Span, Self>;
fn consume(self) -> Self {
self
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Split<Item, Stream, Error> {
Success {
item: Item,
stream: Stream,
},
NotEnoughItem(Stream),
Error(Error),
}
impl<Item, Stream, Error> FromResidual for Split<Item, Stream, Error> {
fn from_residual(residual: Split<Infallible, Stream, Error>) -> Self {
match residual {
Split::Success { .. } => unreachable!(),
Split::NotEnoughItem(stream) => Split::NotEnoughItem(stream),
Split::Error(error) => Split::Error(error),
}
}
}
impl<Item, Stream, Error> Try for Split<Item, Stream, Error> {
type Output = Success<Item, Stream>;
type Residual = Split<Infallible, Stream, Error>;
fn from_output(Success { token, stream }: Self::Output) -> Self {
Split::Success {
item: token,
stream,
}
}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self {
Split::Success {
item: token,
stream,
} => ControlFlow::Continue(Success { token, stream }),
Split::NotEnoughItem(stream) => ControlFlow::Break(Split::NotEnoughItem(stream)),
Split::Error(error) => ControlFlow::Break(Split::Error(error)),
}
}
}
impl<'a> Streaming for &'a [u8] {
type Error = Infallible;
type Item = u8;
type Span = &'a [u8];
fn split_first(self) -> Split<Self::Item, Self, Self::Error> {
if let [first, stream @ ..] = self {
Split::Success {
item: *first,
stream,
}
} else {
Split::NotEnoughItem(self)
}
}
fn split_at(self, mid: usize) -> Split<Self, Self, Self::Error> {
if mid <= self.len() {
let (head, tail) = <[u8]>::split_at(self, mid);
Split::Success {
item: head,
stream: tail,
}
} else {
Split::NotEnoughItem(self)
}
}
fn split_last(self) -> Split<Self::Item, Self, Self::Error> {
if let [head @ .., last] = self {
Split::Success {
item: *last,
stream: head,
}
} else {
Split::NotEnoughItem(self)
}
}
fn all(self) -> Result<Success<Self::Span, Self>, Self::Error> {
Ok(Success {
token: self,
stream: &self[self.len()..],
})
}
fn diff(self, other: &Self) -> Result<Self, Self> {
if let Some(ret) = self
.len()
.checked_sub(other.len())
.filter(|&offset| self[offset..].as_ptr() == other.as_ptr())
.and_then(|offset| self.get(..offset))
{
Ok(ret)
} else {
Err(self)
}
}
}
#[cfg(test)]
mod tests {
use super::{
Split,
Streaming,
};
#[test]
fn split_first_slice() {
let stream = &b"abcd"[..];
let expected = Split::Success {
item: b'a',
stream: &stream[1..],
};
assert_eq!(Streaming::split_first(stream), expected);
let stream = &b""[..];
let expected = Split::NotEnoughItem(stream);
assert_eq!(Streaming::split_first(stream), expected);
}
#[test]
fn split_at_slice() {
let stream = &[0, 1, 2, 3, 4][..];
for n in 0..stream.len() {
let expected = Split::Success {
item: &stream[..n],
stream: &stream[n..],
};
assert_eq!(Streaming::split_at(stream, n), expected);
}
let n = stream.len() + 1;
let expected = Split::NotEnoughItem(stream);
assert_eq!(Streaming::split_at(stream, n), expected);
}
#[test]
fn split_last_slice() {
let stream = &b"abcd"[..];
let expected = Split::Success {
item: b'd',
stream: &stream[..3],
};
assert_eq!(Streaming::split_last(stream), expected);
let stream = &b""[..];
let expected = Split::NotEnoughItem(stream);
assert_eq!(Streaming::split_last(stream), expected);
}
#[test]
fn diff_slice_all() {
let stream = &[0, 1, 2, 3, 4, 5, 6][..];
assert_eq!(stream.diff(&&stream[stream.len()..]), Ok(&stream[..]));
}
#[test]
fn diff_slice_mid() {
let stream = &[0, 1, 2, 3, 4, 5, 6][..];
assert_eq!(
stream.diff(&&stream[stream.len() / 2..]),
Ok(&stream[..stream.len() / 2])
);
}
#[test]
fn diff_slice_error() {
let stream = &[0, 1, 2, 3, 4, 5, 6][..];
assert_eq!(
stream[..stream.len() / 2].as_ref().diff(&&stream[..]),
Err(&stream[..stream.len() / 2])
);
}
}