use std::sync::Arc;
use mineral_protocol::{SessionMessage, SubscribeRequest, SubscriptionId, SubscriptionTopic};
use rustc_hash::FxHashMap;
use tokio::sync::{Notify, mpsc};
use tokio::task::JoinHandle;
use super::session::SessionServices;
use super::updates::{
pump_downloads_detail, pump_downloads_summary, pump_events, pump_pcm, pump_playback,
pump_player, pump_tasks,
};
use crate::client::ClientHandle;
use crate::pcm_relay::{PcmRelay, PcmSubscription};
struct Pump {
handle: JoinHandle<()>,
resync: Arc<Notify>,
pcm: Option<PcmSubscription>,
}
#[derive(Default)]
pub(super) struct Subscriptions {
pumps: FxHashMap<SubscriptionId, Pump>,
}
impl Subscriptions {
pub(super) fn subscribe(
&mut self,
request: &SubscribeRequest,
client: &ClientHandle,
services: &SessionServices,
out: &mpsc::Sender<SessionMessage>,
) {
if self.pumps.contains_key(&request.id) {
mineral_log::warn!(
target: "ipc",
subscription = request.id.value(),
"重复订阅 id,忽略"
);
return;
}
let resync = Arc::new(Notify::new());
let (handle, pcm) = match request.topic {
SubscriptionTopic::Player => {
let known = request.known_player.unwrap_or_default();
let handle = tokio::spawn(pump_player(
client.clone(),
request.id,
known,
Arc::clone(&resync),
out.clone(),
));
(handle, None)
}
SubscriptionTopic::Playback => {
let handle = tokio::spawn(pump_playback(
services.publishers.playback(),
request.id,
out.clone(),
));
(handle, None)
}
SubscriptionTopic::Tasks => {
let handle = tokio::spawn(pump_tasks(
services.publishers.tasks(),
request.id,
out.clone(),
));
(handle, None)
}
SubscriptionTopic::DownloadsSummary => {
let handle = tokio::spawn(pump_downloads_summary(
services.publishers.downloads_summary(),
request.id,
out.clone(),
));
(handle, None)
}
SubscriptionTopic::DownloadsDetail => {
let handle = tokio::spawn(pump_downloads_detail(
services.publishers.downloads_detail(),
services.publishers.downloads_delta(),
request.id,
Arc::clone(&resync),
out.clone(),
));
(handle, None)
}
SubscriptionTopic::Pcm => {
let (token, rx) = services.publishers.pcm().subscribe();
let handle = tokio::spawn(pump_pcm(rx, request.id, out.clone()));
(handle, Some(token))
}
SubscriptionTopic::Events(category) => {
let handle = tokio::spawn(pump_events(
services.publishers.events(),
category,
request.id,
client.clone(),
out.clone(),
));
(handle, None)
}
};
self.pumps.insert(
request.id,
Pump {
handle,
resync,
pcm,
},
);
}
pub(super) fn unsubscribe(&mut self, id: SubscriptionId, pcm: &PcmRelay) {
let Some(pump) = self.pumps.remove(&id) else {
return;
};
pump.handle.abort();
if let Some(token) = pump.pcm {
pcm.unsubscribe(token);
}
}
pub(super) fn resync(&self, id: SubscriptionId) {
if let Some(pump) = self.pumps.get(&id) {
pump.resync.notify_one();
}
}
pub(super) fn shutdown(&mut self, pcm: &PcmRelay) {
for (_id, pump) in self.pumps.drain() {
pump.handle.abort();
if let Some(token) = pump.pcm {
pcm.unsubscribe(token);
}
}
}
}