use std::any::type_name;
use std::convert::Infallible;
use std::num::NonZero;
use bytesbuf::mem::testing::TransparentMemory;
use bytesbuf::mem::{HasMemory, Memory, MemoryShared, OpaqueMemory};
use bytesbuf::{BytesBuf, BytesView};
use crate::Read;
const PREFERRED_READ_SIZE: usize = 100;
#[derive(Debug)]
pub struct FakeRead {
contents: BytesView,
max_read_size: Option<NonZero<usize>>,
memory: OpaqueMemory,
}
impl FakeRead {
#[must_use]
pub fn builder() -> FakeReadBuilder {
FakeReadBuilder {
contents: None,
max_read_size: None,
memory: OpaqueMemory::new(TransparentMemory::new()),
}
}
#[must_use]
pub fn new(contents: BytesView) -> Self {
Self::builder().contents(contents).build()
}
#[cfg_attr(test, mutants::skip)] #[expect(clippy::unused_async, reason = "API compatibility between trait and inherent fn")]
pub async fn read_at_most_into(&mut self, len: usize, mut into: BytesBuf) -> Result<(usize, BytesBuf), Infallible> {
let bytes_to_read = len
.min(self.contents.len())
.min(self.max_read_size.map_or(usize::MAX, NonZero::get));
if bytes_to_read == 0 {
return Ok((0, into));
}
let data_to_read = self.contents.range(0..bytes_to_read);
into.put_bytes(data_to_read);
self.contents.advance(bytes_to_read);
Ok((bytes_to_read, into))
}
#[cfg_attr(test, mutants::skip)] pub async fn read_more_into(&mut self, into: BytesBuf) -> Result<(usize, BytesBuf), Infallible> {
let previous_len = into.len();
self.read_at_most_into(PREFERRED_READ_SIZE, into)
.await
.inspect(|result| debug_assert_eq!(previous_len + result.0, result.1.len()))
}
pub async fn read_any(&mut self) -> Result<BytesBuf, Infallible> {
Ok(self.read_at_most_into(PREFERRED_READ_SIZE, BytesBuf::new()).await?.1)
}
#[must_use]
pub fn memory(&self) -> impl MemoryShared {
self.memory.clone()
}
#[must_use]
pub fn reserve(&self, min_bytes: usize) -> BytesBuf {
self.memory.reserve(min_bytes)
}
}
#[cfg_attr(coverage_nightly, coverage(off))] impl Read for FakeRead {
type Error = Infallible;
#[cfg_attr(test, mutants::skip)] async fn read_at_most_into(&mut self, len: usize, into: BytesBuf) -> Result<(usize, BytesBuf), Infallible> {
self.read_at_most_into(len, into).await
}
#[cfg_attr(test, mutants::skip)] async fn read_more_into(&mut self, into: BytesBuf) -> Result<(usize, BytesBuf), Infallible> {
self.read_more_into(into).await
}
#[cfg_attr(test, mutants::skip)] async fn read_any(&mut self) -> Result<BytesBuf, Infallible> {
self.read_any().await
}
}
#[cfg_attr(coverage_nightly, coverage(off))] impl Memory for FakeRead {
#[cfg_attr(test, mutants::skip)] fn reserve(&self, min_bytes: usize) -> BytesBuf {
self.reserve(min_bytes)
}
}
#[cfg_attr(coverage_nightly, coverage(off))] impl HasMemory for FakeRead {
#[cfg_attr(test, mutants::skip)] fn memory(&self) -> impl MemoryShared {
self.memory()
}
}
#[derive(Debug)]
pub struct FakeReadBuilder {
contents: Option<BytesView>,
max_read_size: Option<NonZero<usize>>,
memory: OpaqueMemory,
}
impl FakeReadBuilder {
#[must_use]
pub fn contents(mut self, contents: BytesView) -> Self {
self.contents = Some(contents);
self
}
#[must_use]
pub fn max_read_size(mut self, max_read_size: NonZero<usize>) -> Self {
self.max_read_size = Some(max_read_size);
self
}
#[must_use]
pub fn memory(mut self, memory: OpaqueMemory) -> Self {
self.memory = memory;
self
}
#[must_use]
pub fn build(self) -> FakeRead {
assert!(self.contents.is_some(), "{} requires a sequence to be set", type_name::<Self>());
FakeRead {
contents: self.contents.expect("guarded by assertion above"),
max_read_size: self.max_read_size,
memory: self.memory,
}
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use bytesbuf::mem::GlobalPool;
use new_zealand::nz;
use testing_aids::async_test;
use super::*;
use crate::ReadExt;
#[test]
fn smoke_test() {
async_test(async || {
let memory = GlobalPool::new();
let mut buf = memory.reserve(100);
buf.put_byte(1);
buf.put_byte(2);
buf.put_byte(3);
let contents = buf.consume_all();
let mut read_stream = FakeRead::new(contents);
let stream_memory = read_stream.reserve(1234);
assert!(stream_memory.capacity() >= 1234);
let mut payload = read_stream.read_exactly(3).await.unwrap();
assert_eq!(payload.len(), 3);
assert_eq!(payload.get_byte(), 1);
assert_eq!(payload.get_byte(), 2);
assert_eq!(payload.get_byte(), 3);
let final_read = read_stream.read_any().await.unwrap();
assert_eq!(final_read.len(), 0);
});
}
#[test]
fn with_max_read_size() {
async_test(async || {
let memory = GlobalPool::new();
let test_data = BytesView::copied_from_slice(b"Hello, world!", &memory);
let mut read_stream = FakeRead::builder().contents(test_data).max_read_size(nz!(2)).build();
let mut payload = read_stream.read_any().await.unwrap().consume_all();
assert_eq!(payload.len(), 2);
assert_eq!(payload.get_byte(), b'H');
assert_eq!(payload.get_byte(), b'e');
payload = read_stream.read_any().await.unwrap().consume_all();
assert_eq!(payload.len(), 2);
assert_eq!(payload.get_byte(), b'l');
assert_eq!(payload.get_byte(), b'l');
payload = read_stream.read_any().await.unwrap().consume_all();
assert_eq!(payload.len(), 2);
assert_eq!(payload.get_byte(), b'o');
assert_eq!(payload.get_byte(), b',');
payload = read_stream.read_any().await.unwrap().consume_all();
assert_eq!(payload.len(), 2);
assert_eq!(payload.get_byte(), b' ');
assert_eq!(payload.get_byte(), b'w');
payload = read_stream.read_any().await.unwrap().consume_all();
assert_eq!(payload.len(), 2);
assert_eq!(payload.get_byte(), b'o');
assert_eq!(payload.get_byte(), b'r');
payload = read_stream.read_any().await.unwrap().consume_all();
assert_eq!(payload.len(), 2);
assert_eq!(payload.get_byte(), b'l');
assert_eq!(payload.get_byte(), b'd');
payload = read_stream.read_any().await.unwrap().consume_all();
assert_eq!(payload.len(), 1);
assert_eq!(payload.get_byte(), b'!');
});
}
#[test]
fn read_more() {
async_test(async || {
let memory = GlobalPool::new();
let test_data = BytesView::copied_from_slice(b"Hello, world!", &memory);
let mut read_stream = FakeRead::builder().contents(test_data.clone()).max_read_size(nz!(2)).build();
let mut payload_buffer = read_stream.reserve(100);
loop {
let previous_len = payload_buffer.len();
let (bytes_read, new_payload_buffer) = read_stream.read_more_into(payload_buffer).await.unwrap();
payload_buffer = new_payload_buffer;
assert_eq!(previous_len + bytes_read, payload_buffer.len());
if bytes_read == 0 {
break;
}
}
assert_eq!(payload_buffer.len(), test_data.len());
let mut payload = payload_buffer.consume_all();
assert_eq!(payload.len(), test_data.len());
assert_eq!(payload.get_byte(), b'H');
assert_eq!(payload.get_byte(), b'e');
assert_eq!(payload.get_byte(), b'l');
assert_eq!(payload.get_byte(), b'l');
assert_eq!(payload.get_byte(), b'o');
assert_eq!(payload.get_byte(), b',');
assert_eq!(payload.get_byte(), b' ');
assert_eq!(payload.get_byte(), b'w');
assert_eq!(payload.get_byte(), b'o');
assert_eq!(payload.get_byte(), b'r');
assert_eq!(payload.get_byte(), b'l');
assert_eq!(payload.get_byte(), b'd');
assert_eq!(payload.get_byte(), b'!');
assert_eq!(payload.len(), 0);
});
}
#[test]
fn memory_returns_configured_provider() {
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use bytesbuf::mem::testing::TransparentMemory;
use bytesbuf::mem::{CallbackMemory, OpaqueMemory};
let callback_called = Arc::new(AtomicBool::new(false));
let custom_memory = OpaqueMemory::new(CallbackMemory::new({
let callback_called = Arc::clone(&callback_called);
move |min_bytes| {
callback_called.store(true, Ordering::SeqCst);
TransparentMemory::new().reserve(min_bytes)
}
}));
let memory = GlobalPool::new();
let contents = BytesView::copied_from_slice(b"test", &memory);
let read_stream = FakeRead::builder().contents(contents).memory(custom_memory).build();
let stream_memory = read_stream.memory();
let _buf = stream_memory.reserve(10);
assert!(
callback_called.load(Ordering::SeqCst),
"Custom memory callback should have been called"
);
}
}