use crate::ShutdownToken;
use async_trait::async_trait;
use std::time::Duration;
#[async_trait]
pub trait FrameSource: Send {
async fn next_frame(&mut self) -> Frame;
async fn close(&mut self) {}
}
pub enum Frame {
Inbound(Vec<String>),
Tick,
Closed,
}
pub async fn pump<S, D, Fut>(
source: &mut S,
idle_timeout: Duration,
shutdown: &ShutdownToken,
mut dispatch: D,
) -> nagisa_types::error::Result<bool>
where
S: FrameSource + ?Sized,
D: FnMut(String) -> Fut,
Fut: std::future::Future<Output = bool>,
{
let idle = tokio::time::sleep(idle_timeout);
tokio::pin!(idle);
loop {
tokio::select! {
biased;
_ = shutdown.cancelled() => {
source.close().await;
return Ok(true);
}
_ = &mut idle => {
tracing::warn!(timeout = ?idle_timeout, "event source idle (no inbound); reconnecting");
return Ok(false);
}
frame = source.next_frame() => {
match frame {
Frame::Inbound(payloads) => {
idle.as_mut().reset(tokio::time::Instant::now() + idle_timeout);
for payload in payloads {
if dispatch(payload).await {
return Ok(true);
}
}
}
Frame::Tick => {}
Frame::Closed => return Ok(false),
}
}
}
}
}