use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use bytes::Bytes;
use tokio::io::{AsyncRead, ReadBuf};
pub struct BlobReader {
buffer: Bytes,
position: usize,
}
impl BlobReader {
#[must_use]
pub fn from_bytes(buffer: Bytes) -> Self {
Self {
buffer,
position: 0,
}
}
#[must_use]
pub fn empty() -> Self {
Self {
buffer: Bytes::new(),
position: 0,
}
}
#[must_use]
pub fn from_slice(data: &[u8]) -> Self {
Self {
buffer: Bytes::copy_from_slice(data),
position: 0,
}
}
#[must_use]
pub fn len(&self) -> Option<u64> {
Some(self.buffer.len() as u64)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
#[must_use]
pub fn bytes_read(&self) -> u64 {
self.position as u64
}
#[must_use]
pub fn remaining(&self) -> Option<u64> {
self.len()
.map(|total| total.saturating_sub(self.position as u64))
}
pub fn rewind(&mut self) {
self.position = 0;
}
#[must_use]
pub fn into_bytes(self) -> Bytes {
self.buffer
}
#[must_use]
pub fn as_bytes(&self) -> &Bytes {
&self.buffer
}
#[must_use]
pub fn unread_slice(&self) -> &[u8] {
&self.buffer[self.position..]
}
#[must_use]
pub fn is_exhausted(&self) -> bool {
self.position >= self.buffer.len()
}
}
impl Default for BlobReader {
fn default() -> Self {
Self::empty()
}
}
impl AsyncRead for BlobReader {
fn poll_read(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let remaining = self.buffer.len().saturating_sub(self.position);
if remaining == 0 {
return Poll::Ready(Ok(()));
}
let to_read = remaining.min(buf.remaining());
let end = self.position + to_read;
buf.put_slice(&self.buffer[self.position..end]);
self.position = end;
Poll::Ready(Ok(()))
}
}
impl std::fmt::Debug for BlobReader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BlobReader")
.field("total_len", &self.buffer.len())
.field("position", &self.position)
.field("remaining", &self.remaining())
.finish()
}
}
impl Clone for BlobReader {
fn clone(&self) -> Self {
Self {
buffer: self.buffer.clone(),
position: 0, }
}
}
impl From<Bytes> for BlobReader {
fn from(bytes: Bytes) -> Self {
Self::from_bytes(bytes)
}
}
impl From<Vec<u8>> for BlobReader {
fn from(vec: Vec<u8>) -> Self {
Self::from_bytes(Bytes::from(vec))
}
}
impl From<&[u8]> for BlobReader {
fn from(slice: &[u8]) -> Self {
Self::from_slice(slice)
}
}
impl From<&str> for BlobReader {
fn from(s: &str) -> Self {
Self::from_slice(s.as_bytes())
}
}
impl From<String> for BlobReader {
fn from(s: String) -> Self {
Self::from_bytes(Bytes::from(s))
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use tokio::io::AsyncReadExt;
#[test]
fn test_blob_reader_creation() {
let data = Bytes::from_static(b"Hello, World!");
let reader = BlobReader::from_bytes(data);
assert_eq!(reader.len(), Some(13));
assert!(!reader.is_empty());
assert_eq!(reader.bytes_read(), 0);
assert_eq!(reader.remaining(), Some(13));
}
#[test]
fn test_blob_reader_empty() {
let reader = BlobReader::empty();
assert_eq!(reader.len(), Some(0));
assert!(reader.is_empty());
assert!(reader.is_exhausted());
}
#[test]
fn test_blob_reader_from_slice() {
let reader = BlobReader::from_slice(b"test data");
assert_eq!(reader.len(), Some(9));
assert_eq!(reader.as_bytes().as_ref(), b"test data");
}
#[tokio::test]
async fn test_blob_reader_read_all() {
let data = Bytes::from_static(b"Hello, World!");
let mut reader = BlobReader::from_bytes(data);
let mut output = Vec::new();
reader.read_to_end(&mut output).await.unwrap();
assert_eq!(output, b"Hello, World!");
assert_eq!(reader.bytes_read(), 13);
assert!(reader.is_exhausted());
}
#[tokio::test]
async fn test_blob_reader_read_chunked() {
let data = Bytes::from_static(b"0123456789ABCDEF");
let mut reader = BlobReader::from_bytes(data);
let mut buffer = [0u8; 4];
let n = reader.read(&mut buffer).await.unwrap();
assert_eq!(n, 4);
assert_eq!(&buffer, b"0123");
assert_eq!(reader.bytes_read(), 4);
let n = reader.read(&mut buffer).await.unwrap();
assert_eq!(n, 4);
assert_eq!(&buffer, b"4567");
assert_eq!(reader.bytes_read(), 8);
let mut remaining = Vec::new();
reader.read_to_end(&mut remaining).await.unwrap();
assert_eq!(remaining, b"89ABCDEF");
assert!(reader.is_exhausted());
}
#[tokio::test]
async fn test_blob_reader_empty_read() {
let mut reader = BlobReader::empty();
let mut buffer = [0u8; 10];
let n = reader.read(&mut buffer).await.unwrap();
assert_eq!(n, 0); }
#[test]
fn test_blob_reader_rewind() {
let data = Bytes::from_static(b"test");
let mut reader = BlobReader::from_bytes(data);
reader.position = 4;
assert!(reader.is_exhausted());
reader.rewind();
assert_eq!(reader.bytes_read(), 0);
assert!(!reader.is_exhausted());
}
#[test]
fn test_blob_reader_into_bytes() {
let data = Bytes::from_static(b"Hello");
let reader = BlobReader::from_bytes(data.clone());
let recovered = reader.into_bytes();
assert_eq!(recovered, data);
}
#[test]
fn test_blob_reader_unread_slice() {
let data = Bytes::from_static(b"Hello");
let mut reader = BlobReader::from_bytes(data);
assert_eq!(reader.unread_slice(), b"Hello");
reader.position = 2;
assert_eq!(reader.unread_slice(), b"llo");
}
#[test]
fn test_blob_reader_clone() {
let data = Bytes::from_static(b"test");
let mut original = BlobReader::from_bytes(data);
original.position = 2;
let cloned = original.clone();
assert_eq!(cloned.bytes_read(), 0);
assert_eq!(original.bytes_read(), 2);
assert_eq!(cloned.as_bytes(), original.as_bytes());
}
#[test]
fn test_blob_reader_from_conversions() {
let from_vec: BlobReader = vec![1u8, 2, 3].into();
assert_eq!(from_vec.len(), Some(3));
let from_slice: BlobReader = b"hello".as_slice().into();
assert_eq!(from_slice.len(), Some(5));
let from_str: BlobReader = "world".into();
assert_eq!(from_str.len(), Some(5));
let from_string: BlobReader = String::from("test").into();
assert_eq!(from_string.len(), Some(4));
}
#[test]
fn test_blob_reader_debug() {
let reader = BlobReader::from_bytes(Bytes::from_static(b"test"));
let debug = format!("{reader:?}");
assert!(debug.contains("BlobReader"));
assert!(debug.contains("total_len"));
assert!(debug.contains("position"));
}
#[tokio::test]
async fn test_blob_reader_large_data() {
let data: Vec<u8> = (0..10000).map(|i| (i % 256) as u8).collect();
let mut reader = BlobReader::from_bytes(Bytes::from(data.clone()));
let mut output = Vec::new();
reader.read_to_end(&mut output).await.unwrap();
assert_eq!(output, data);
}
}