use std::mem;
use crate::{Lock, Lockable, Sendable, Shared, Weak};
struct CallbackCollection<T> {
callbacks: Vec<WeakCallback<T>>,
}
impl<T> Default for CallbackCollection<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> CallbackCollection<T> {
fn new() -> Self {
Self {
callbacks: Vec::new(),
}
}
fn len(&self) -> usize {
self.callbacks.len()
}
fn contains(&self, ptr: CallbackPtr<T>) -> bool {
for callback in &self.callbacks {
if callback.callback.as_ptr() == ptr {
return true;
}
}
false
}
fn insert(&mut self, callback: WeakCallback<T>) {
if !self.contains(callback.callback.as_ptr()) {
self.callbacks.push(callback);
}
}
fn remove(&mut self, ptr: CallbackPtr<T>) {
let equals = |callback: &WeakCallback<T>| callback.callback.as_ptr() != ptr;
self.callbacks.retain(equals);
}
}
impl<T> IntoIterator for CallbackCollection<T> {
type Item = WeakCallback<T>;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.callbacks.into_iter()
}
}
#[cfg(feature = "multithread")]
type RawCallback<'a, T> = dyn FnMut(&T) + Send + 'a;
#[cfg(not(feature = "multithread"))]
type RawCallback<'a, T> = dyn FnMut(&T) + 'a;
type CallbackPtr<T> = *const Lock<RawCallback<'static, T>>;
type Callbacks<T> = Lock<CallbackCollection<T>>;
#[derive(Clone)]
pub struct Callback<'a, T = ()> {
callback: Shared<Lock<RawCallback<'a, T>>>,
}
impl<'a, T> Callback<'a, T> {
pub fn new(callback: impl FnMut(&T) + Sendable + 'a) -> Self {
Self {
callback: Shared::new(Lock::new(callback)),
}
}
pub fn downgrade(&self) -> WeakCallback<T> {
let callback = unsafe {
mem::transmute::<Weak<Lock<RawCallback<'a, T>>>, Weak<Lock<RawCallback<'static, T>>>>(
Shared::downgrade(&self.callback),
)
};
WeakCallback { callback }
}
pub fn emit(&self, event: &T) {
self.callback.lock_mut()(event);
}
}
impl<'a, T> Default for Callback<'a, T> {
fn default() -> Self {
Callback::new(|_| {})
}
}
#[derive(Clone)]
pub struct WeakCallback<T = ()> {
callback: Weak<Lock<RawCallback<'static, T>>>,
}
impl<T> WeakCallback<T> {
pub fn new(weak: Weak<Lock<RawCallback<'static, T>>>) -> Self {
Self { callback: weak }
}
pub fn upgrade(&self) -> Option<Callback<T>> {
Some(Callback {
callback: self.callback.upgrade()?,
})
}
pub fn as_ptr(&self) -> CallbackPtr<T> {
self.callback.as_ptr() as CallbackPtr<T>
}
pub fn emit(&self, event: &T) -> bool {
if let Some(callback) = self.upgrade() {
callback.emit(event);
}
self.callback.strong_count() > 0
}
}
impl<T> Default for WeakCallback<T> {
fn default() -> Self {
Callback::default().downgrade()
}
}
pub struct CallbackEmitter<T = ()> {
callbacks: Shared<Callbacks<T>>,
}
impl<T> Default for CallbackEmitter<T> {
fn default() -> Self {
Self {
callbacks: Shared::new(Lock::new(CallbackCollection::new())),
}
}
}
impl<T> Clone for CallbackEmitter<T> {
fn clone(&self) -> Self {
Self {
callbacks: self.callbacks.clone(),
}
}
}
impl<T> CallbackEmitter<T> {
pub fn new() -> Self {
Self::default()
}
pub fn len(&self) -> usize {
self.callbacks.lock_mut().len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn downgrade(&self) -> WeakCallbackEmitter<T> {
WeakCallbackEmitter {
callbacks: Shared::downgrade(&self.callbacks),
}
}
pub fn subscribe(&self, callback: &Callback<'_, T>) {
self.subscribe_weak(callback.downgrade());
}
pub fn subscribe_weak(&self, callback: WeakCallback<T>) {
self.callbacks.lock_mut().insert(callback);
}
pub fn unsubscribe(&self, ptr: CallbackPtr<T>) {
self.callbacks.lock_mut().remove(ptr);
}
pub fn emit(&self, event: &T) {
let callbacks = mem::take(&mut *self.callbacks.lock_mut());
for callback in callbacks.into_iter() {
if let Some(callback) = callback.upgrade() {
callback.emit(event);
}
}
}
}
impl CallbackEmitter {
pub fn track(&self) {
self.downgrade().track();
}
}
pub struct WeakCallbackEmitter<T = ()> {
callbacks: Weak<Callbacks<T>>,
}
impl<T> WeakCallbackEmitter<T> {
pub fn upgrade(&self) -> Option<CallbackEmitter<T>> {
Some(CallbackEmitter {
callbacks: self.callbacks.upgrade()?,
})
}
}
impl<T> Clone for WeakCallbackEmitter<T> {
fn clone(&self) -> Self {
Self {
callbacks: self.callbacks.clone(),
}
}
}
impl WeakCallbackEmitter {
pub fn track(&self) {
super::effect::track_callback(self.clone());
}
}