irosh 0.2.0

SSH sessions over Iroh peer-to-peer transport
Documentation
//! Async read/write adapters for Iroh streams.

use std::pin::Pin;
use std::task::{Context, Poll};

use iroh::endpoint::{RecvStream, SendStream};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};

/// An adapter wrapping an Iroh send and receive stream pair into a single
/// type implementing [`AsyncRead`] and [`AsyncWrite`].
///
/// This permits seamless integration between Iroh's native QUIC abstractions
/// and higher-level network protocols expecting bidirectional duplex streams,
/// such as `russh`.
pub struct IrohDuplex {
    send: SendStream,
    recv: RecvStream,
}

impl IrohDuplex {
    /// Creates a new `IrohDuplex` from an Iroh send/recv stream pair.
    pub fn new(send: SendStream, recv: RecvStream) -> Self {
        Self { send, recv }
    }
}

impl AsyncRead for IrohDuplex {
    fn poll_read(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut ReadBuf<'_>,
    ) -> Poll<std::io::Result<()>> {
        Pin::new(&mut self.recv).poll_read(cx, buf)
    }
}

impl AsyncWrite for IrohDuplex {
    fn poll_write(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &[u8],
    ) -> Poll<std::io::Result<usize>> {
        match Pin::new(&mut self.send).poll_write(cx, buf) {
            Poll::Ready(Ok(written)) => Poll::Ready(Ok(written)),
            Poll::Ready(Err(err)) => Poll::Ready(Err(std::io::Error::other(err))),
            Poll::Pending => Poll::Pending,
        }
    }

    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
        match Pin::new(&mut self.send).poll_flush(cx) {
            Poll::Ready(Ok(())) => Poll::Ready(Ok(())),
            Poll::Ready(Err(err)) => Poll::Ready(Err(std::io::Error::other(err))),
            Poll::Pending => Poll::Pending,
        }
    }

    fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
        match Pin::new(&mut self.send).poll_shutdown(cx) {
            Poll::Ready(Ok(())) => Poll::Ready(Ok(())),
            Poll::Ready(Err(err)) => Poll::Ready(Err(std::io::Error::other(err))),
            Poll::Pending => Poll::Pending,
        }
    }
}

// Ensure at compile time that the struct holds the Send trait bound
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_irohduplex_send_sync() {
        fn assert_send_sync<T: Send + Sync>() {}
        assert_send_sync::<IrohDuplex>();
    }
}