use std::{
any::TypeId,
fmt,
sync::{
Arc,
atomic::{AtomicU64, Ordering},
},
};
use reovim_arch::sync::Mutex;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SubscriptionId(u64);
impl SubscriptionId {
pub(crate) fn new() -> Self {
static COUNTER: AtomicU64 = AtomicU64::new(1);
Self(COUNTER.fetch_add(1, Ordering::Relaxed))
}
#[inline]
#[must_use]
pub const fn as_u64(&self) -> u64 {
self.0
}
}
impl fmt::Display for SubscriptionId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "sub-{}", self.0)
}
}
type UnsubscribeFn = Box<dyn FnOnce() + Send + Sync>;
pub struct Subscription {
id: SubscriptionId,
type_id: TypeId,
type_name: &'static str,
unsubscribe_fn: Arc<Mutex<Option<UnsubscribeFn>>>,
}
impl Subscription {
pub(crate) fn new<E: 'static>(
id: SubscriptionId,
unsubscribe_fn: impl FnOnce() + Send + Sync + 'static,
) -> Self {
Self {
id,
type_id: TypeId::of::<E>(),
type_name: std::any::type_name::<E>(),
unsubscribe_fn: Arc::new(Mutex::new(Some(Box::new(unsubscribe_fn)))),
}
}
#[inline]
#[must_use]
pub const fn id(&self) -> SubscriptionId {
self.id
}
#[inline]
#[must_use]
pub const fn type_id(&self) -> TypeId {
self.type_id
}
#[inline]
#[must_use]
pub const fn type_name(&self) -> &'static str {
self.type_name
}
#[must_use]
pub fn is_active(&self) -> bool {
self.unsubscribe_fn.lock().is_some()
}
pub fn cancel(&self) {
let unsub = self.unsubscribe_fn.lock().take();
if let Some(callback) = unsub {
callback();
}
}
pub fn detach(&self) {
let _ = self.unsubscribe_fn.lock().take();
}
}
impl Drop for Subscription {
fn drop(&mut self) {
self.cancel();
}
}
impl fmt::Debug for Subscription {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Subscription")
.field("id", &self.id)
.field("type_name", &self.type_name)
.field("active", &self.is_active())
.finish_non_exhaustive()
}
}