matchmaker/
selection.rs

1use crate::{Identifier, Selection};
2use indexmap::IndexMap;
3use rustc_hash::FxBuildHasher;
4use std::sync::Mutex;
5use std::{borrow::Borrow, hash::Hash, sync::Arc};
6
7pub type SelectionValidator<S> = fn(&S) -> bool;
8
9#[derive(Debug)]
10pub struct Selector<T, S> {
11    selections: Option<SelectorImpl<u32, S>>,
12    pub identifier: Identifier<T, S>,
13    pub validator: SelectionValidator<S>,
14}
15
16pub fn truthy_validator<S>(_: &S) -> bool {
17    true
18}
19
20impl<T, S: Selection> Selector<T, S> {
21    pub fn new(identifier: Identifier<T, S>) -> Self {
22        Self::new_with_validator(identifier, truthy_validator)
23    }
24
25    pub fn new_with_validator(
26        identifier: Identifier<T, S>,
27        validator: SelectionValidator<S>,
28    ) -> Self {
29        Self {
30            selections: Some(SelectorImpl::new()),
31            identifier,
32            validator,
33        }
34    }
35
36    pub fn disabled(mut self) -> Self {
37        self.selections = None;
38        self
39    }
40
41    // --------------------------------------------
42
43    pub fn sel(&mut self, item: &T) -> bool {
44        let Some(selections) = &mut self.selections else {
45            return false;
46        };
47
48        let (k, v) = (self.identifier)(item);
49        selections.insert(k, v)
50    }
51
52    pub fn desel(&mut self, item: &T) -> bool {
53        let Some(selections) = &mut self.selections else {
54            return false;
55        };
56
57        let (k, _v) = (self.identifier)(item);
58        selections.remove(&k)
59    }
60
61    pub fn contains(&self, item: &T) -> bool {
62        let Some(selections) = &self.selections else {
63            return false;
64        };
65
66        let (k, _v) = (self.identifier)(item);
67        selections.contains(&k)
68    }
69
70    pub fn toggle(&mut self, item: &T) {
71        let Some(selections) = &mut self.selections else {
72            return;
73        };
74
75        let (k, v) = (self.identifier)(item);
76        if selections.contains(&k) {
77            selections.remove(&k);
78        } else {
79            selections.insert(k, v);
80        }
81    }
82
83    pub fn clear(&mut self) {
84        if let Some(selections) = &mut self.selections {
85            selections.clear();
86        }
87    }
88
89    pub fn len(&self) -> usize {
90        self.selections.as_ref().map_or(0, |s| s.len())
91    }
92
93    // -----------------------------------------------------
94
95    pub fn is_empty(&self) -> bool {
96        self.selections.as_ref().is_none_or(|s| s.is_empty())
97    }
98
99    pub fn output(&mut self) -> impl Iterator<Item = S> {
100        if let Some(selections) = &mut self.selections {
101            let mut set = selections.set.lock().unwrap();
102            std::mem::take(&mut *set).into_values()
103        } else {
104            IndexMap::with_capacity(0).into_values()
105        }
106    }
107
108    pub fn identify_to_vec<I>(&self, items: I) -> Vec<S>
109    where
110        I: IntoIterator,
111        I::Item: std::borrow::Borrow<T> + Send,
112    {
113        items
114            .into_iter()
115            .map(|item| (self.identifier)(item.borrow()).1)
116            .collect()
117    }
118
119    pub fn map_to_vec<U, F>(&self, f: F) -> Vec<U>
120    where
121        F: FnMut(&S) -> U,
122    {
123        self.selections
124            .as_ref()
125            .map_or_else(Vec::new, |s| s.map_to_vec(f))
126    }
127
128    pub fn revalidate(&mut self) {
129        let Some(selections) = &mut self.selections else {
130            return;
131        };
132
133        let mut set = selections.set.lock().unwrap();
134        let validator = &self.validator;
135
136        set.retain(|_, v| validator(v));
137    }
138
139    pub fn cycle_all_bg<I>(&self, items: I)
140    where
141        I: IntoIterator,
142        I::Item: std::borrow::Borrow<T> + Send,
143    {
144        let Some(selections) = &self.selections else {
145            return;
146        };
147
148        let results: Vec<_> = items
149            .into_iter()
150            .map(|item| (self.identifier)(item.borrow()))
151            .collect();
152
153        let selections = selections.clone();
154
155        #[cfg(feature = "parallelism")]
156        tokio::task::spawn_blocking(move || {
157            let mut set_guard = selections.set.lock().unwrap();
158
159            let mut all = true;
160            let mut seen = 0;
161            for (i, (k, _)) in results.iter().enumerate() {
162                if !set_guard.contains_key(k) {
163                    all = false;
164                    seen = i;
165                    break;
166                }
167            }
168
169            if all {
170                for (k, _) in results {
171                    set_guard.swap_remove(&k);
172                }
173            } else {
174                for (k, v) in results.into_iter().skip(seen) {
175                    set_guard.insert(k, v);
176                }
177            }
178        });
179
180        #[cfg(not(feature = "parallelism"))]
181        {
182            let mut set_guard = selections.set.lock().unwrap();
183
184            let mut all = true;
185            let mut seen = 0;
186            for (i, (k, _)) in results.iter().enumerate() {
187                if !set_guard.contains_key(k) {
188                    all = false;
189                    seen = i;
190                    break;
191                }
192            }
193
194            if all {
195                for (k, _) in results {
196                    set_guard.swap_remove(&k);
197                }
198            } else {
199                for (k, v) in results.into_iter().skip(seen) {
200                    set_guard.insert(k, v);
201                }
202            }
203        }
204    }
205}
206
207// ---------- Selection Set ---------------
208#[derive(Debug, Clone)]
209struct SelectorImpl<K: Eq + Hash, S> {
210    pub set: Arc<Mutex<IndexMap<K, S, FxBuildHasher>>>,
211}
212
213impl<K: Eq + Hash, S> SelectorImpl<K, S>
214where
215    S: Selection,
216{
217    pub fn new() -> Self {
218        Self {
219            set: Arc::new(Mutex::new(IndexMap::with_hasher(FxBuildHasher))),
220        }
221    }
222
223    pub fn insert(&self, key: K, value: S) -> bool {
224        let mut set = self.set.lock().unwrap();
225        set.insert(key, value).is_none()
226    }
227
228    pub fn remove(&self, key: &K) -> bool {
229        let mut set = self.set.lock().unwrap();
230        set.shift_remove(key).is_some()
231    }
232
233    pub fn contains(&self, key: &K) -> bool {
234        let set = self.set.lock().unwrap();
235        set.contains_key(key)
236    }
237
238    pub fn clear(&self) {
239        let mut set = self.set.lock().unwrap();
240        set.clear();
241    }
242
243    pub fn clone(&self) -> Self {
244        Self {
245            set: Arc::clone(&self.set),
246        }
247    }
248
249    pub fn len(&self) -> usize {
250        let set = self.set.lock().unwrap();
251        set.len()
252    }
253
254    pub fn is_empty(&self) -> bool {
255        let set = self.set.lock().unwrap();
256        set.is_empty()
257    }
258
259    pub fn map_to_vec<U, F>(&self, f: F) -> Vec<U>
260    where
261        F: FnMut(&S) -> U,
262    {
263        let set = self.set.lock().unwrap();
264        set.values().map(f).collect()
265    }
266}