use std::pin::Pin;
use std::sync::{Arc, Mutex, MutexGuard, PoisonError, Weak};
use std::task::{Context, Poll, Waker};
use futures::Stream;
use oino_core::events::ClientEvent;
use oino_core::hub::EventStream;
fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
mutex.lock().unwrap_or_else(PoisonError::into_inner)
}
#[derive(Default)]
struct Slot {
next: Option<(EventStream, ClientEvent)>,
closed: bool,
waker: Option<Waker>,
}
#[derive(Default)]
pub(crate) struct Followers {
slots: Mutex<Vec<Weak<Mutex<Slot>>>>,
}
impl Followers {
pub(crate) fn stream(&self, current: EventStream) -> FollowStream {
let slot = Arc::new(Mutex::new(Slot::default()));
lock(&self.slots).push(Arc::downgrade(&slot));
FollowStream {
current,
slot,
announce: None,
}
}
pub(crate) fn close(&self) {
for weak in lock(&self.slots).drain(..) {
if let Some(slot) = weak.upgrade() {
let mut slot = lock(&slot);
slot.closed = true;
if let Some(waker) = slot.waker.take() {
waker.wake();
}
}
}
}
pub(crate) fn rebind(&self, subscribe: impl Fn() -> EventStream, announce: &ClientEvent) {
lock(&self.slots).retain(|weak| {
let Some(slot) = weak.upgrade() else {
return false;
};
let mut slot = lock(&slot);
slot.next = Some((subscribe(), announce.clone()));
if let Some(waker) = slot.waker.take() {
waker.wake();
}
true
});
}
}
pub struct FollowStream {
current: EventStream,
slot: Arc<Mutex<Slot>>,
announce: Option<ClientEvent>,
}
impl std::fmt::Debug for FollowStream {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FollowStream").finish_non_exhaustive()
}
}
impl Stream for FollowStream {
type Item = ClientEvent;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<ClientEvent>> {
loop {
if let Some(event) = self.announce.take() {
return Poll::Ready(Some(event));
}
if let Poll::Ready(Some(event)) = Pin::new(&mut self.current).poll_next(cx) {
return Poll::Ready(Some(event));
}
let next = {
let mut slot = lock(&self.slot);
let next = slot.next.take();
if next.is_none() && slot.closed {
return Poll::Ready(None);
}
if next.is_none() {
slot.waker = Some(cx.waker().clone());
}
next
};
match next {
Some((stream, announce)) => {
self.current = stream;
self.announce = Some(announce);
}
None => return Poll::Pending,
}
}
}
}