laputa-blocking 0.3.0

A thread pool for isolating blocking I/O in async programs
Documentation
use blocking::Unblock;
use futures_lite::{future, io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}};
use std::io::{self, Cursor, ErrorKind, Read, Seek, SeekFrom, Write};

struct Interrupted {
    inner: Cursor<Vec<u8>>,
    read: bool,
    write: bool,
    flush: bool,
    seek: bool,
}

fn once(flag: &mut bool) -> io::Result<()> {
    if std::mem::take(flag) {
        Err(ErrorKind::Interrupted.into())
    } else {
        Ok(())
    }
}

impl Read for Interrupted {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        once(&mut self.read)?;
        self.inner.read(buf)
    }
}

impl Write for Interrupted {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        once(&mut self.write)?;
        self.inner.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        once(&mut self.flush)?;
        self.inner.flush()
    }
}

impl Seek for Interrupted {
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
        once(&mut self.seek)?;
        self.inner.seek(pos)
    }
}

#[test]
fn interruptions_are_retried_without_losing_bytes() {
    future::block_on(async {
        let mut io = Unblock::new(Interrupted {
            inner: Cursor::new(Vec::new()), read: true, write: true, flush: true, seek: true,
        });
        io.write_all(b"hello").await.unwrap();
        io.flush().await.unwrap();
        io.seek(SeekFrom::Start(0)).await.unwrap();
        let mut bytes = Vec::new();
        io.read_to_end(&mut bytes).await.unwrap();
        assert_eq!(bytes, b"hello");
    });
}

#[test]
fn full_writer_reports_write_zero() {
    future::block_on(async {
        let mut io = Unblock::new(Cursor::new([0u8; 1]));
        io.write_all(b"AB").await.unwrap();
        assert_eq!(io.flush().await.unwrap_err().kind(), ErrorKind::WriteZero);
    });
}

#[test]
fn standalone_flush_retries_interruption() {
    future::block_on(async {
        let mut io = Unblock::new(Interrupted {
            inner: Cursor::new(Vec::new()), read: false, write: false, flush: true, seek: false,
        });
        io.flush().await.unwrap();
    });
}