web-sys-async-io 0.5.0

An async-IO implementation read/write stream in the browser, using the web-sys types.
use std::{
    future::Future,
    pin::Pin,
    task::{ready, Poll},
};

use wasm_bindgen_futures::JsFuture;

#[derive(Debug, Default)]
pub enum Op {
    #[default]
    Idle,
    Write(JsFuture, usize),
    Flush(JsFuture),
    Shutdown(JsFuture),
}

#[derive(Debug)]
pub struct Writer {
    pub inner: web_sys::WritableStreamDefaultWriter,
    pub op: Op,
}

impl Writer {
    pub fn new(inner: web_sys::WritableStreamDefaultWriter) -> Self {
        Self {
            inner,
            op: Op::default(),
        }
    }
}

impl tokio::io::AsyncWrite for Writer {
    fn poll_write(
        mut self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
        buf: &[u8],
    ) -> std::task::Poll<Result<usize, std::io::Error>> {
        match self.op {
            Op::Write(ref mut fut, size) => {
                let result = ready!(Pin::new(fut).poll(cx));
                self.op = Op::Idle;
                Poll::Ready(result.map(|_| size).map_err(super::js_value_to_io_error))
            }
            Op::Idle => {
                let chunk = js_sys::Uint8Array::from(buf);
                let fut = JsFuture::from(self.inner.write_with_chunk(chunk.as_ref()));
                self.op = Op::Write(fut, buf.len());
                self.poll_write(cx, buf)
            }
            _ => Poll::Pending,
        }
    }

    fn poll_flush(
        mut self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Result<(), std::io::Error>> {
        match self.op {
            Op::Flush(ref mut fut) => {
                let result = ready!(Pin::new(fut).poll(cx));
                self.op = Op::Idle;
                Poll::Ready(result.map(|_| ()).map_err(super::js_value_to_io_error))
            }
            Op::Idle => {
                let fut = JsFuture::from(self.inner.ready());
                self.op = Op::Flush(fut);
                self.poll_flush(cx)
            }
            _ => Poll::Pending,
        }
    }

    fn poll_shutdown(
        mut self: std::pin::Pin<&mut Self>,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Result<(), std::io::Error>> {
        match self.op {
            Op::Shutdown(ref mut fut) => {
                let result = ready!(Pin::new(fut).poll(cx));
                self.op = Op::Idle;
                Poll::Ready(result.map(|_| ()).map_err(super::js_value_to_io_error))
            }
            Op::Idle => {
                let fut = JsFuture::from(self.inner.close());
                self.op = Op::Shutdown(fut);
                self.poll_shutdown(cx)
            }
            _ => Poll::Pending,
        }
    }
}