use async_trait::async_trait;
use mofa_kernel::agent::secretary::UserConnection;
use tokio::sync::mpsc;
pub struct ChannelConnection<I, O> {
input_rx: tokio::sync::Mutex<mpsc::Receiver<I>>,
output_tx: mpsc::Sender<O>,
connected: std::sync::atomic::AtomicBool,
}
impl<I, O> ChannelConnection<I, O>
where
I: Send + 'static,
O: Send + 'static,
{
pub fn new(input_rx: mpsc::Receiver<I>, output_tx: mpsc::Sender<O>) -> Self {
Self {
input_rx: tokio::sync::Mutex::new(input_rx),
output_tx,
connected: std::sync::atomic::AtomicBool::new(true),
}
}
pub fn new_pair(buffer_size: usize) -> (Self, mpsc::Sender<I>, mpsc::Receiver<O>) {
let (input_tx, input_rx) = mpsc::channel(buffer_size);
let (output_tx, output_rx) = mpsc::channel(buffer_size);
let conn = Self::new(input_rx, output_tx);
(conn, input_tx, output_rx)
}
}
#[async_trait]
impl<I, O> UserConnection for ChannelConnection<I, O>
where
I: Send + 'static,
O: Send + 'static,
{
type Input = I;
type Output = O;
async fn receive(&self) -> anyhow::Result<Self::Input> {
let mut rx = self.input_rx.lock().await;
rx.recv()
.await
.ok_or_else(|| anyhow::anyhow!("Channel closed"))
}
async fn try_receive(&self) -> anyhow::Result<Option<Self::Input>> {
let mut rx = self.input_rx.lock().await;
match rx.try_recv() {
Ok(input) => Ok(Some(input)),
Err(mpsc::error::TryRecvError::Empty) => Ok(None),
Err(mpsc::error::TryRecvError::Disconnected) => {
self.connected
.store(false, std::sync::atomic::Ordering::SeqCst);
Err(anyhow::anyhow!("Channel disconnected"))
}
}
}
async fn send(&self, output: Self::Output) -> anyhow::Result<()> {
self.output_tx
.send(output)
.await
.map_err(|_| anyhow::anyhow!("Failed to send output"))
}
fn is_connected(&self) -> bool {
self.connected.load(std::sync::atomic::Ordering::SeqCst) && !self.output_tx.is_closed()
}
async fn close(&self) -> anyhow::Result<()> {
self.connected
.store(false, std::sync::atomic::Ordering::SeqCst);
Ok(())
}
}
pub struct TimeoutConnection<C> {
inner: C,
receive_timeout_ms: u64,
send_timeout_ms: u64,
}
impl<C> TimeoutConnection<C> {
pub fn new(inner: C, receive_timeout_ms: u64, send_timeout_ms: u64) -> Self {
Self {
inner,
receive_timeout_ms,
send_timeout_ms,
}
}
}
#[async_trait]
impl<C> UserConnection for TimeoutConnection<C>
where
C: UserConnection,
{
type Input = C::Input;
type Output = C::Output;
async fn receive(&self) -> anyhow::Result<Self::Input> {
tokio::time::timeout(
tokio::time::Duration::from_millis(self.receive_timeout_ms),
self.inner.receive(),
)
.await
.map_err(|_| anyhow::anyhow!("Receive timeout"))?
}
async fn try_receive(&self) -> anyhow::Result<Option<Self::Input>> {
self.inner.try_receive().await
}
async fn send(&self, output: Self::Output) -> anyhow::Result<()> {
tokio::time::timeout(
tokio::time::Duration::from_millis(self.send_timeout_ms),
self.inner.send(output),
)
.await
.map_err(|_| anyhow::anyhow!("Send timeout"))?
}
fn is_connected(&self) -> bool {
self.inner.is_connected()
}
async fn close(&self) -> anyhow::Result<()> {
self.inner.close().await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_channel_connection() {
let (conn, input_tx, mut output_rx) = ChannelConnection::<String, String>::new_pair(10);
input_tx.send("Hello".to_string()).await.unwrap();
let input = conn.receive().await.unwrap();
assert_eq!(input, "Hello");
conn.send("World".to_string()).await.unwrap();
let output = output_rx.recv().await.unwrap();
assert_eq!(output, "World");
assert!(conn.is_connected());
}
#[tokio::test]
async fn test_try_receive() {
let (conn, _input_tx, _output_rx) = ChannelConnection::<String, String>::new_pair(10);
let result = conn.try_receive().await.unwrap();
assert!(result.is_none());
}
}