use bytes::Bytes;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{self, AsyncWrite, ErrorKind};
use tokio::sync::mpsc;
pub(crate) struct WritableChannel {
pub write_tx: mpsc::Sender<Bytes>,
}
impl AsyncWrite for WritableChannel {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
let write_tx = self.write_tx.clone();
let bytes = Bytes::copy_from_slice(buf);
match write_tx.try_send(bytes.clone()) {
Ok(()) => Poll::Ready(Ok(buf.len())),
Err(mpsc::error::TrySendError::Full(_)) => {
let waker = cx.waker().clone();
tokio::spawn(async move {
if write_tx.send(bytes).await.is_ok() {
waker.wake();
}
});
Poll::Pending
}
Err(mpsc::error::TrySendError::Closed(_)) => Poll::Ready(Err(std::io::Error::new(
ErrorKind::BrokenPipe,
"Channel closed",
))),
}
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}
#[cfg(test)]
mod tests {
use super::WritableChannel;
use bytes::Bytes;
use tokio::io::{AsyncWriteExt, ErrorKind};
use tokio::sync::mpsc;
#[tokio::test]
async fn test_write_successful() {
let (tx, mut rx) = mpsc::channel(1);
let mut writer = WritableChannel { write_tx: tx };
let data = b"hello world";
let n = writer.write(data).await.unwrap();
assert_eq!(n, data.len());
let received = rx.recv().await.unwrap();
assert_eq!(received, Bytes::from_static(data));
}
#[tokio::test]
async fn test_write_when_channel_full() {
let (tx, mut rx) = mpsc::channel(1);
tx.send(Bytes::from_static(b"pre-filled")).await.unwrap();
let mut writer = WritableChannel { write_tx: tx };
let data = b"deferred";
let write_future = writer.write(data);
let _ = rx.recv().await;
let n = write_future.await.unwrap();
assert_eq!(n, data.len());
let received = rx.recv().await.unwrap();
assert_eq!(received, Bytes::from_static(data));
}
#[tokio::test]
async fn test_write_after_channel_closed() {
let (tx, rx) = mpsc::channel(1);
drop(rx);
let mut writer = WritableChannel { write_tx: tx };
let result = writer.write(b"data").await;
assert!(result.is_err());
let err = result.unwrap_err();
assert_eq!(err.kind(), ErrorKind::BrokenPipe);
}
#[tokio::test]
async fn test_poll_flush_and_shutdown() {
let (tx, _rx) = mpsc::channel(1);
let mut writer = WritableChannel { write_tx: tx };
writer.flush().await.unwrap();
writer.shutdown().await.unwrap();
}
}