safe_http_async 0.1.0-beta.4

Simple and safe asynchronous HTTP types.
Documentation
use super::Body;
use std::{
    io::{self, Cursor, Read},
    pin::Pin,
    task::{Context, Poll},
};

#[derive(Debug)]
pub(crate) struct TestBody {
    chunks: Cursor<Vec<Cursor<Vec<u8>>>>,
}

impl TestBody {
    pub fn new<C>(chunks: C) -> Self
    where
        C: IntoIterator,
        C::Item: IntoIterator<Item = u8>,
    {
        Self {
            chunks: Cursor::new(
                chunks
                    .into_iter()
                    .map(|chunk| Cursor::new(chunk.into_iter().collect()))
                    .collect(),
            ),
        }
    }

    fn increment_chunks_position(&mut self) {
        let next_position = self.chunks.position() + 1;
        self.chunks.set_position(next_position);
    }
}

impl From<&'_ Vec<Vec<u8>>> for TestBody {
    fn from(chunks: &'_ Vec<Vec<u8>>) -> Self {
        Self::new(chunks.iter().map(|chunk| chunk.iter().copied()))
    }
}

impl Body for TestBody {
    fn poll_chunk(
        mut self: Pin<&mut Self>,
        _cx: &mut Context<'_>,
        buf: &mut [u8],
    ) -> Poll<io::Result<usize>> {
        let chunk_pos = usize_position(&self.chunks);
        let chunk = match self.chunks.get_mut().get_mut(chunk_pos) {
            Some(x) => x,
            None => return Poll::Ready(Ok(0)),
        };
        let read_amount = chunk.read(buf)?;
        if read_amount == 0 {
            self.increment_chunks_position();
            Poll::Ready(Ok(0))
        } else {
            Poll::Ready(Ok(read_amount))
        }
    }

    fn poll_chunk_size_hint(
        self: Pin<&mut Self>,
        _cx: &mut Context<'_>,
    ) -> Poll<io::Result<(usize, Option<usize>)>> {
        let chunk_pos = usize_position(&self.chunks);
        let size = self.chunks.get_ref().get(chunk_pos).map_or(0, chunk_size);
        Poll::Ready(Ok(exact_size_hint(size)))
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let chunk_pos = usize_position(&self.chunks);
        let chunks = &self.chunks.get_ref()[chunk_pos..];
        let size = chunks.iter().map(chunk_size).sum();
        exact_size_hint(size)
    }
}

fn usize_position<T>(cursor: &Cursor<T>) -> usize {
    usize_from(cursor.position())
}

fn usize_from(x: u64) -> usize {
    x.try_into().unwrap()
}

fn chunk_size(chunk: &Cursor<Vec<u8>>) -> usize {
    chunk.get_ref().len() - usize_position(chunk)
}

fn exact_size_hint(size: usize) -> (usize, Option<usize>) {
    (size, Some(size))
}