use std::io::{Read, Write}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; /// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or /// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`]. #[derive(Debug)] pub struct SyncIoBridge { src: T, rt: tokio::runtime::Handle, } impl Read for SyncIoBridge { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let src = &mut self.src; self.rt.block_on(AsyncReadExt::read(src, buf)) } fn read_to_end(&mut self, buf: &mut Vec) -> std::io::Result { let src = &mut self.src; self.rt.block_on(src.read_to_end(buf)) } fn read_to_string(&mut self, buf: &mut String) -> std::io::Result { let src = &mut self.src; self.rt.block_on(src.read_to_string(buf)) } fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> { let src = &mut self.src; // The AsyncRead trait returns the count, synchronous doesn't. let _n = self.rt.block_on(src.read_exact(buf))?; Ok(()) } } impl Write for SyncIoBridge { fn write(&mut self, buf: &[u8]) -> std::io::Result { let src = &mut self.src; self.rt.block_on(src.write(buf)) } fn flush(&mut self) -> std::io::Result<()> { let src = &mut self.src; self.rt.block_on(src.flush()) } fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { let src = &mut self.src; self.rt.block_on(src.write_all(buf)) } fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result { let src = &mut self.src; self.rt.block_on(src.write_vectored(bufs)) } } // Because https://doc.rust-lang.org/std/io/trait.Write.html#method.is_write_vectored is at the time // of this writing still unstable, we expose this as part of a standalone method. impl SyncIoBridge { /// Determines if the underlying [`tokio::io::AsyncWrite`] target supports efficient vectored writes. /// /// See [`tokio::io::AsyncWrite::is_write_vectored`]. pub fn is_write_vectored(&self) -> bool { self.src.is_write_vectored() } } impl SyncIoBridge { /// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or /// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`]. /// /// When this struct is created, it captures a handle to the current thread's runtime with [`tokio::runtime::Handle::current`]. /// It is hence OK to move this struct into a separate thread outside the runtime, as created /// by e.g. [`tokio::task::spawn_blocking`]. /// /// Stated even more strongly: to make use of this bridge, you *must* move /// it into a separate thread outside the runtime. The synchronous I/O will use the /// underlying handle to block on the backing asynchronous source, via /// [`tokio::runtime::Handle::block_on`]. As noted in the documentation for that /// function, an attempt to `block_on` from an asynchronous execution context /// will panic. /// /// # Wrapping `!Unpin` types /// /// Use e.g. `SyncIoBridge::new(Box::pin(src))`. /// /// # Panic /// /// This will panic if called outside the context of a Tokio runtime. pub fn new(src: T) -> Self { Self::new_with_handle(src, tokio::runtime::Handle::current()) } /// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or /// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`]. /// /// This is the same as [`SyncIoBridge::new`], but allows passing an arbitrary handle and hence may /// be initially invoked outside of an asynchronous context. pub fn new_with_handle(src: T, rt: tokio::runtime::Handle) -> Self { Self { src, rt } } }