use crate::error::Error;
use crate::queue::Queue;
use crate::ringbuf::{PushOutcome, RingBuffer};
pub enum ChannelBuffer {
Lossy(RingBuffer),
Reliable(Queue),
}
impl ChannelBuffer {
pub fn push(&self, frame: &[u8]) -> Result<usize, Error> {
match self {
Self::Lossy(rb) => Ok(rb.push(frame)),
Self::Reliable(q) => q.push(frame).map(|()| 0),
}
}
pub fn push_checked(&self, frame: &[u8]) -> Result<PushOutcome, Error> {
match self {
Self::Lossy(rb) => Ok(rb.push_checked(frame)),
Self::Reliable(q) => q.push(frame).map(|()| PushOutcome::Accepted(0)),
}
}
#[must_use]
pub fn drain_all(&self) -> Vec<u8> {
match self {
Self::Lossy(rb) => rb.drain_all(),
Self::Reliable(q) => q.drain_all(),
}
}
#[must_use]
pub fn try_pop(&self) -> Option<Vec<u8>> {
match self {
Self::Lossy(rb) => rb.try_pop(),
Self::Reliable(q) => q.try_pop(),
}
}
#[must_use]
pub fn frame_count(&self) -> usize {
match self {
Self::Lossy(rb) => rb.frame_count(),
Self::Reliable(q) => q.frame_count(),
}
}
#[must_use]
pub fn bytes_used(&self) -> usize {
match self {
Self::Lossy(rb) => rb.bytes_used(),
Self::Reliable(q) => q.bytes_used(),
}
}
pub fn clear(&self) {
match self {
Self::Lossy(rb) => rb.clear(),
Self::Reliable(q) => q.clear(),
}
}
#[must_use]
pub fn is_ordered(&self) -> bool {
matches!(self, Self::Reliable(_))
}
}
impl std::fmt::Debug for ChannelBuffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Lossy(rb) => f.debug_tuple("ChannelBuffer::Lossy").field(rb).finish(),
Self::Reliable(q) => f.debug_tuple("ChannelBuffer::Reliable").field(q).finish(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lossy_delegates_correctly() {
let cb = ChannelBuffer::Lossy(RingBuffer::new(1024));
let dropped = cb.push(b"alpha").unwrap();
assert_eq!(dropped, 0);
cb.push(b"beta").unwrap();
assert_eq!(cb.frame_count(), 2);
assert_eq!(cb.try_pop().unwrap(), b"alpha");
assert_eq!(cb.try_pop().unwrap(), b"beta");
assert!(cb.try_pop().is_none());
assert!(!cb.is_ordered());
}
#[test]
fn reliable_delegates_correctly() {
let cb = ChannelBuffer::Reliable(Queue::new(1024));
let dropped = cb.push(b"alpha").unwrap();
assert_eq!(dropped, 0);
cb.push(b"beta").unwrap();
assert_eq!(cb.frame_count(), 2);
assert_eq!(cb.try_pop().unwrap(), b"alpha");
assert_eq!(cb.try_pop().unwrap(), b"beta");
assert!(cb.try_pop().is_none());
assert!(cb.is_ordered());
}
#[test]
fn push_lossy_never_errors() {
let cb = ChannelBuffer::Lossy(RingBuffer::new(8));
assert_eq!(cb.push(b"aaaa").unwrap(), 0);
assert_eq!(cb.push(b"bbbb").unwrap(), 1);
assert_eq!(cb.frame_count(), 1);
assert_eq!(cb.try_pop().unwrap(), b"bbbb");
}
#[test]
fn push_reliable_errors_when_full() {
let cb = ChannelBuffer::Reliable(Queue::new(16));
cb.push(b"aaaa").unwrap(); cb.push(b"bbbb").unwrap();
let err = cb.push(b"cccc").unwrap_err();
assert!(matches!(err, Error::ChannelFull));
assert_eq!(cb.frame_count(), 2);
}
#[test]
fn push_checked_lossy() {
let cb = ChannelBuffer::Lossy(RingBuffer::new(8));
assert_eq!(cb.push_checked(b"aaaa").unwrap(), PushOutcome::Accepted(0));
assert_eq!(cb.push_checked(b"bbbb").unwrap(), PushOutcome::Accepted(1));
assert_eq!(cb.push_checked(&[0u8; 100]).unwrap(), PushOutcome::TooLarge);
}
#[test]
fn push_checked_reliable() {
let cb = ChannelBuffer::Reliable(Queue::new(16));
assert_eq!(cb.push_checked(b"aaaa").unwrap(), PushOutcome::Accepted(0));
assert_eq!(cb.push_checked(b"bbbb").unwrap(), PushOutcome::Accepted(0));
let err = cb.push_checked(b"cccc").unwrap_err();
assert!(matches!(err, Error::ChannelFull));
}
#[test]
fn drain_format_identical() {
let lossy = ChannelBuffer::Lossy(RingBuffer::new(1024));
let reliable = ChannelBuffer::Reliable(Queue::new(1024));
let frames: &[&[u8]] = &[b"hello", b"world", b"test"];
for frame in frames {
lossy.push(frame).unwrap();
reliable.push(frame).unwrap();
}
let lossy_blob = lossy.drain_all();
let reliable_blob = reliable.drain_all();
assert_eq!(lossy_blob, reliable_blob);
assert!(!lossy_blob.is_empty());
}
#[test]
fn clear_delegates() {
let cb = ChannelBuffer::Reliable(Queue::new(1024));
cb.push(b"one").unwrap();
cb.push(b"two").unwrap();
assert_eq!(cb.frame_count(), 2);
cb.clear();
assert_eq!(cb.frame_count(), 0);
assert_eq!(cb.bytes_used(), 0);
}
#[test]
fn bytes_used_delegates() {
let cb = ChannelBuffer::Lossy(RingBuffer::new(1024));
assert_eq!(cb.bytes_used(), 0);
cb.push(b"abc").unwrap(); assert_eq!(cb.bytes_used(), 7);
}
}