use crate::{Observer, Snapshot};
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use core::{
cell::OnceCell,
fmt,
sync::atomic::{AtomicBool, Ordering},
};
pub struct PollEvent<'a> {
observer: &'a Observer,
snapshot: OnceCell<Arc<Snapshot>>,
}
impl<'a> PollEvent<'a> {
fn new(observer: &'a Observer) -> Self {
Self {
observer,
snapshot: OnceCell::new(),
}
}
pub fn snapshot(&self) -> &Snapshot {
self.snapshot
.get_or_init(|| Arc::new(self.observer.snapshot()))
.as_ref()
}
pub fn try_snapshot(&self) -> Option<&Snapshot> {
if self.snapshot.get().is_none() {
let snapshot = Arc::new(self.observer.try_snapshot()?);
let _ = self.snapshot.set(snapshot);
}
self.snapshot.get().map(Arc::as_ref)
}
pub fn snapshot_owned(&self) -> Arc<Snapshot> {
self.snapshot();
Arc::clone(self.snapshot.get().expect("snapshot was just built"))
}
pub fn observer(&self) -> &Observer {
self.observer
}
pub fn snapshot_materialized(&self) -> bool {
self.snapshot.get().is_some()
}
}
impl fmt::Debug for PollEvent<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PollEvent")
.field("snapshot_materialized", &self.snapshot_materialized())
.finish_non_exhaustive()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SubscriptionId(usize);
type LocalCallback<'a> = Box<dyn FnMut(&PollEvent<'_>) + 'a>;
type SharedCallback = Box<dyn Fn(&PollEvent<'_>) + Send + Sync>;
pub struct LocalPoller<'a> {
observer: Observer,
subscribers: Vec<(SubscriptionId, LocalCallback<'a>)>,
next_id: usize,
}
impl<'a> LocalPoller<'a> {
pub fn new(observer: Observer) -> Self {
Self {
observer,
subscribers: Vec::new(),
next_id: 0,
}
}
pub fn subscribe(&mut self, callback: impl FnMut(&PollEvent<'_>) + 'a) -> SubscriptionId {
let id = SubscriptionId(self.next_id);
self.next_id = self
.next_id
.checked_add(1)
.expect("subscription identifiers exhausted");
self.subscribers.push((id, Box::new(callback)));
id
}
pub fn unsubscribe(&mut self, id: SubscriptionId) -> bool {
let before = self.subscribers.len();
self.subscribers.retain(|(key, _)| *key != id);
before != self.subscribers.len()
}
pub fn poll(&mut self) {
let event = PollEvent::new(&self.observer);
for (_, callback) in &mut self.subscribers {
callback(&event);
}
}
}
impl fmt::Debug for LocalPoller<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LocalPoller")
.field("subscribers", &self.subscribers.len())
.finish_non_exhaustive()
}
}
struct SharedInner {
observer: Observer,
subscribers: Vec<SharedCallback>,
dispatching: AtomicBool,
}
#[derive(Clone)]
pub struct SharedPoller {
inner: Arc<SharedInner>,
}
impl SharedPoller {
pub fn new(
observer: Observer,
callback: impl Fn(&PollEvent<'_>) + Send + Sync + 'static,
) -> Self {
let mut builder = Self::builder(observer);
builder.subscribe(callback);
builder.build()
}
pub fn builder(observer: Observer) -> SharedPollerBuilder {
SharedPollerBuilder {
observer,
subscribers: Vec::new(),
}
}
pub fn try_poll(&self) -> bool {
if self
.inner
.dispatching
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
return false;
}
struct Claim<'a>(&'a AtomicBool);
impl Drop for Claim<'_> {
fn drop(&mut self) {
self.0.store(false, Ordering::Release);
}
}
let _claim = Claim(&self.inner.dispatching);
let event = PollEvent::new(&self.inner.observer);
for callback in &self.inner.subscribers {
callback(&event);
}
true
}
}
impl fmt::Debug for SharedPoller {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SharedPoller")
.field("subscribers", &self.inner.subscribers.len())
.finish_non_exhaustive()
}
}
pub struct SharedPollerBuilder {
observer: Observer,
subscribers: Vec<SharedCallback>,
}
impl SharedPollerBuilder {
pub fn subscribe(
&mut self,
callback: impl Fn(&PollEvent<'_>) + Send + Sync + 'static,
) -> &mut Self {
self.subscribers.push(Box::new(callback));
self
}
pub fn build(self) -> SharedPoller {
SharedPoller {
inner: Arc::new(SharedInner {
observer: self.observer,
subscribers: self.subscribers,
dispatching: AtomicBool::new(false),
}),
}
}
}
impl fmt::Debug for SharedPollerBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SharedPollerBuilder")
.field("subscribers", &self.subscribers.len())
.finish_non_exhaustive()
}
}