use std::time::Duration;
use rmcp::RoleServer;
use rmcp::model::{ProgressNotificationParam, ProgressToken};
use rmcp::service::Peer;
use tokio::sync::mpsc::Receiver;
use tokio::task::JoinHandle;
use super::Frame;
pub(crate) const FLUSH_GRACE: Duration = Duration::from_millis(250);
#[derive(Debug)]
#[must_use = "the frames a run queued are delivered by the pump, so finish it"]
pub(crate) struct ProgressPump {
task: JoinHandle<()>,
}
impl ProgressPump {
pub(crate) fn spawn(
frames: Receiver<Frame>,
peer: Peer<RoleServer>,
token: ProgressToken,
) -> ProgressPump {
ProgressPump {
task: tokio::spawn(pump(frames, peer, token)),
}
}
#[cfg(test)]
pub(crate) fn from_task(task: JoinHandle<()>) -> ProgressPump {
ProgressPump { task }
}
pub(crate) async fn finish(mut self) {
match tokio::time::timeout(FLUSH_GRACE, &mut self.task).await {
Ok(Ok(())) => {}
Ok(Err(error)) => {
tracing::debug!(%error, "the progress pump did not finish cleanly");
}
Err(_elapsed) => {
self.task.abort();
let _aborted = (&mut self.task).await;
tracing::debug!("abandoned the progress pump, which was still sending");
}
}
}
}
async fn pump(mut frames: Receiver<Frame>, peer: Peer<RoleServer>, token: ProgressToken) {
while let Some(frame) = frames.recv().await {
let notification = ProgressNotificationParam::new(token.clone(), f64::from(frame.progress))
.with_message(frame.message);
if let Err(error) = peer.notify_progress(notification).await {
tracing::debug!(%error, "stopped reporting progress");
return;
}
}
}