use std::time::Duration;
use tokio::sync::oneshot;
use crate::client::WriterHandle;
use crate::error::ClientError;
pub struct IdleHandle {
writer: WriterHandle,
reply_rx: oneshot::Receiver<Result<Vec<u8>, ClientError>>,
}
impl IdleHandle {
pub(crate) fn new(
writer: WriterHandle,
reply_rx: oneshot::Receiver<Result<Vec<u8>, ClientError>>,
) -> Self {
Self { writer, reply_rx }
}
pub async fn stop(self) -> Result<(), ClientError> {
self.stop_with_timeout(Duration::from_secs(30)).await
}
pub async fn stop_with_timeout(self, done_timeout: Duration) -> Result<(), ClientError> {
self.writer.send_raw(b"DONE\r\n".to_vec()).await?;
match tokio::time::timeout(done_timeout, self.reply_rx).await {
Ok(Ok(Ok(_frame))) => Ok(()),
Ok(Ok(Err(e))) => Err(e),
Ok(Err(_)) => Err(ClientError::ConnectionClosed),
Err(_) => Err(ClientError::Timeout),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::client::RawClient;
use tokio::io::{AsyncReadExt, AsyncWriteExt, duplex};
#[tokio::test]
async fn test_idle_stop_sends_done_and_awaits_ok() {
let (client_io, mut server_io) = duplex(1024);
let mut raw = RawClient::new(client_io);
let writer = raw.writer();
let (_tag, reply_rx) = raw.send_command_async("IDLE").await.unwrap();
let handle = IdleHandle::new(writer, reply_rx);
let mut buf = [0u8; 1024];
let n = server_io.read(&mut buf).await.unwrap();
let cmd = String::from_utf8_lossy(&buf[..n]).into_owned();
assert!(cmd.contains("IDLE"));
let tag = cmd.split_whitespace().next().unwrap().to_string();
server_io.write_all(b"+ idling\r\n").await.unwrap();
let stop_task = tokio::spawn(async move { handle.stop().await });
let n = server_io.read(&mut buf).await.unwrap();
assert_eq!(&buf[..n], b"DONE\r\n");
server_io
.write_all(format!("{} OK IDLE terminated\r\n", tag).as_bytes())
.await
.unwrap();
stop_task.await.unwrap().unwrap();
}
}