1use std::sync::Arc;
2use std::{hash::Hash, sync::Weak};
3
4use parking_lot::Mutex;
5
6use collections::{btree_map, BTreeMap, HashMap};
7
8use crate::MutableAppContext;
9
10pub type Mapping<K, F> = Mutex<HashMap<K, BTreeMap<usize, Option<F>>>>;
11
12pub struct CallbackCollection<K: Hash + Eq, F> {
13 internal: Arc<Mapping<K, F>>,
14}
15
16impl<K: Hash + Eq, F> Clone for CallbackCollection<K, F> {
17 fn clone(&self) -> Self {
18 Self {
19 internal: self.internal.clone(),
20 }
21 }
22}
23
24impl<K: Hash + Eq + Copy, F> Default for CallbackCollection<K, F> {
25 fn default() -> Self {
26 CallbackCollection {
27 internal: Arc::new(Mutex::new(Default::default())),
28 }
29 }
30}
31
32impl<K: Hash + Eq + Copy, F> CallbackCollection<K, F> {
33 pub fn downgrade(&self) -> Weak<Mapping<K, F>> {
34 Arc::downgrade(&self.internal)
35 }
36
37 #[cfg(test)]
38 pub fn is_empty(&self) -> bool {
39 self.internal.lock().is_empty()
40 }
41
42 pub fn add_callback(&mut self, id: K, subscription_id: usize, callback: F) {
43 self.internal
44 .lock()
45 .entry(id)
46 .or_default()
47 .insert(subscription_id, Some(callback));
48 }
49
50 pub fn remove(&mut self, id: K) {
51 self.internal.lock().remove(&id);
52 }
53
54 pub fn add_or_remove_callback(&mut self, id: K, subscription_id: usize, callback: F) {
55 match self
56 .internal
57 .lock()
58 .entry(id)
59 .or_default()
60 .entry(subscription_id)
61 {
62 btree_map::Entry::Vacant(entry) => {
63 entry.insert(Some(callback));
64 }
65
66 btree_map::Entry::Occupied(entry) => {
67 // TODO: This seems like it should never be called because no code
68 // should ever attempt to remove an existing callback
69 debug_assert!(entry.get().is_none());
70 entry.remove();
71 }
72 }
73 }
74
75 pub fn emit_and_cleanup<C: FnMut(&mut F, &mut MutableAppContext) -> bool>(
76 &mut self,
77 id: K,
78 cx: &mut MutableAppContext,
79 mut call_callback: C,
80 ) {
81 let callbacks = self.internal.lock().remove(&id);
82 if let Some(callbacks) = callbacks {
83 for (subscription_id, callback) in callbacks {
84 if let Some(mut callback) = callback {
85 let alive = call_callback(&mut callback, cx);
86 if alive {
87 match self
88 .internal
89 .lock()
90 .entry(id)
91 .or_default()
92 .entry(subscription_id)
93 {
94 btree_map::Entry::Vacant(entry) => {
95 entry.insert(Some(callback));
96 }
97 btree_map::Entry::Occupied(entry) => {
98 entry.remove();
99 }
100 }
101 }
102 }
103 }
104 }
105 }
106}