use std::sync::{Arc, OnceLock};
use dashmap::DashMap;
use crate::ReverseChannel;
use crate::session_manager::SessionManager;
#[derive(Clone, Default)]
pub struct Dropper {
inner: Arc<Inner>,
}
#[derive(Default)]
struct Inner {
banned: DashMap<String, ()>,
ctx: OnceLock<TeardownCtx>,
}
struct TeardownCtx {
sessions: Arc<SessionManager>,
reverse_channel: Option<ReverseChannel>,
}
impl std::fmt::Debug for Dropper {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Dropper").finish_non_exhaustive()
}
}
impl Dropper {
pub fn new() -> Self {
Self::default()
}
pub async fn drop(&self, response_id: String) {
self.inner.banned.insert(response_id.clone(), ());
if let Some(ctx) = self.inner.ctx.get() {
teardown(&response_id, &ctx.sessions, ctx.reverse_channel.as_ref()).await;
}
}
pub(crate) fn is_banned(&self, response_id: &str) -> bool {
self.inner.banned.contains_key(response_id)
}
pub(crate) fn wire(
&self,
sessions: Arc<SessionManager>,
reverse_channel: Option<ReverseChannel>,
) {
let _ = self.inner.ctx.set(TeardownCtx {
sessions,
reverse_channel,
});
}
}
pub(crate) async fn teardown(
response_id: &str,
sessions: &SessionManager,
reverse_channel: Option<&ReverseChannel>,
) {
let Some(session) = sessions.remove(response_id) else {
return;
};
let uses_objectiveai_mcp = session.connections.values().any(|u| u.is_ws());
if uses_objectiveai_mcp {
if let Some(rc) = reverse_channel {
rc.drop_response(response_id.to_string()).await;
}
}
}