use crate::reactive::map::KeyOf;
use amethystate_core::SignalSubscription;
use futures_core::Stream;
use std::collections::VecDeque;
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, Weak};
use std::task::{Context, Poll, Waker};
use uuid::Uuid;
#[derive(Default)]
struct Wake {
waker: Mutex<Option<Waker>>,
ended: AtomicBool,
}
impl Wake {
fn signal(&self) {
if let Some(waker) = self.waker.lock().unwrap().take() {
waker.wake();
}
}
fn end(&self) {
self.ended.store(true, Ordering::Release);
self.signal();
}
fn has_ended(&self) -> bool {
self.ended.load(Ordering::Acquire)
}
fn park(&self, cx: &Context<'_>) {
*self.waker.lock().unwrap() = Some(cx.waker().clone());
}
}
#[doc(hidden)]
#[derive(Default)]
pub struct Streams(Mutex<Vec<Weak<Wake>>>);
impl Streams {
fn follow(&self, wake: &Arc<Wake>) {
let mut followed = self.0.lock().unwrap_or_else(|e| e.into_inner());
followed.retain(|one| one.strong_count() > 0);
followed.push(Arc::downgrade(wake));
}
}
impl Drop for Streams {
fn drop(&mut self) {
let followed = std::mem::take(&mut *self.0.lock().unwrap_or_else(|e| e.into_inner()));
for wake in followed.iter().filter_map(Weak::upgrade) {
wake.end();
}
}
}
pub trait Watchable {
type Item: Clone + Send + 'static;
fn watch_id(&self) -> Uuid;
fn filterable(_item: &Self::Item) -> bool {
true
}
#[track_caller]
fn watch_raw<F>(&self, callback: F) -> SignalSubscription
where
F: Fn(&Self::Item, Option<Uuid>) + Send + Sync + 'static;
#[doc(hidden)]
fn streams(&self) -> Option<&Streams> {
None
}
}
#[must_use = "a Watch subscribes to nothing until register() is called"]
pub struct Watch<W> {
source: W,
external: bool,
}
impl<W: Watchable> Watch<W> {
pub(crate) fn new(source: W) -> Self {
Self {
source,
external: false,
}
}
#[track_caller]
pub fn register<F>(self, callback: F) -> SignalSubscription
where
F: Fn(&W::Item) + Send + Sync + 'static,
{
self.register_with_source(move |item, _| callback(item))
}
#[track_caller]
pub fn register_with_source<F>(self, callback: F) -> SignalSubscription
where
F: Fn(&W::Item, Option<Uuid>) + Send + Sync + 'static,
{
let mine = self.external.then(|| self.source.watch_id());
self.source.watch_raw(move |item, source| {
if mine.is_some() && source == mine && W::filterable(item) {
return;
}
callback(item, source);
})
}
}
impl<W: Watchable> Watch<W> {
#[track_caller]
pub fn stream(self) -> ChangeStream<W::Item> {
let mine = self.external.then(|| self.source.watch_id());
let queue: Arc<Mutex<VecDeque<W::Item>>> = Arc::new(Mutex::new(VecDeque::new()));
let wake = Arc::new(Wake::default());
let sink = Arc::clone(&queue);
let signal = Arc::clone(&wake);
if let Some(streams) = self.source.streams() {
streams.follow(&wake);
}
let sub = self.source.watch_raw(move |item, source| {
if mine.is_some() && source == mine && W::filterable(item) {
return;
}
sink.lock().unwrap().push_back(item.clone());
signal.signal();
});
ChangeStream {
queue,
wake,
_sub: sub,
}
}
}
#[must_use = "dropping the stream ends the subscription"]
pub struct ChangeStream<T> {
queue: Arc<Mutex<VecDeque<T>>>,
wake: Arc<Wake>,
_sub: SignalSubscription,
}
impl<T> Stream for ChangeStream<T> {
type Item = T;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
if let Some(item) = self.queue.lock().unwrap().pop_front() {
return Poll::Ready(Some(item));
}
self.wake.park(cx);
match self.queue.lock().unwrap().pop_front() {
Some(item) => Poll::Ready(Some(item)),
None if self.wake.has_ended() => Poll::Ready(None),
None => Poll::Pending,
}
}
}
impl<W> Watch<W> {
pub fn external(mut self) -> Self {
self.external = true;
self
}
}
impl<K, V> Watch<crate::ReactiveMap<K, V>>
where
K: crate::ReactiveMapKey,
V: crate::ReactiveMapValue,
{
pub fn key(self, key: K) -> Watch<KeyOf<K, V>> {
Watch {
source: KeyOf::new(self.source, key),
external: self.external,
}
}
}