vapix 0.1.0

Client for AXIS Communications devices' VAPIX API
Documentation
use futures::Stream;
use std::future::Future;
use std::pin::Pin;
use std::sync::Mutex;
use std::task::{Context, Poll};

pub fn mock_device<F, B, E>(f: F) -> crate::Device<impl crate::Transport<Error = E>>
where
    F: FnMut(http::Request<Vec<u8>>) -> Result<http::Response<B>, E>,
    B: IntoIterator<Item = Vec<u8>>,
    B::IntoIter: Unpin,
    E: std::error::Error + Unpin,
{
    let t = TransportAdapter(Mutex::new(f));
    let uri = http::Uri::from_static("http://1.2.3.4");
    crate::Device::new(t, uri)
}

struct TransportAdapter<F>(Mutex<F>);

impl<F, E, B> crate::Transport for TransportAdapter<F>
where
    F: FnMut(http::Request<Vec<u8>>) -> Result<http::Response<B>, E>,
    B: IntoIterator<Item = Vec<u8>>,
    B::IntoIter: Unpin,
    E: std::error::Error + Unpin,
{
    type Error = E;
    type Output = TransportAdapterOutput<B::IntoIter, E>;
    type Body = TransportAdapterBody<B::IntoIter, E>;
    type Chunk = Vec<u8>;

    fn roundtrip(&self, request: http::Request<Vec<u8>>) -> Self::Output {
        let result = self.0.lock().unwrap()(request);
        TransportAdapterOutput(Some(result.map(|resp| {
            let (resp, body) = resp.into_parts();
            let body_iter = body.into_iter();
            http::Response::from_parts(resp, TransportAdapterBody(Some(Ok(body_iter))))
        })))
    }
}

struct TransportAdapterOutput<B, E>(Option<Result<http::Response<TransportAdapterBody<B, E>>, E>>);

impl<B: Iterator<Item = Vec<u8>> + Unpin, E: Unpin> Future for TransportAdapterOutput<B, E> {
    type Output = Result<http::Response<TransportAdapterBody<B, E>>, E>;

    fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
        Poll::Ready(self.0.take().expect("poll() once"))
    }
}

struct TransportAdapterBody<B, E>(Option<Result<B, E>>);

impl<B: Iterator<Item = Vec<u8>> + Unpin, E: Unpin> Stream for TransportAdapterBody<B, E> {
    type Item = Result<Vec<u8>, E>;

    fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        match self.0.take() {
            Some(Ok(mut i)) => match i.next() {
                Some(chunk) => {
                    self.0 = Some(Ok(i));
                    Poll::Ready(Some(Ok(chunk)))
                }
                None => Poll::Ready(None),
            },
            Some(Err(e)) => Poll::Ready(Some(Err(e))),
            None => Poll::Ready(None),
        }
    }
}