1use std::cmp::Ordering;
2use std::hash::Hash;
3use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering};
4
5static NEXT_QUERY_POLICY_GENERATION: AtomicU64 = AtomicU64::new(1);
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum TreeChildren<'a, Id> {
13 Leaf,
15 Unloaded,
17 Loading,
19 Loaded(&'a [Id]),
21}
22
23impl<'a, Id> TreeChildren<'a, Id> {
24 #[must_use]
26 pub const fn loaded(children: &'a [Id]) -> Self {
27 if children.is_empty() {
28 Self::Leaf
29 } else {
30 Self::Loaded(children)
31 }
32 }
33
34 #[must_use]
36 pub const fn loaded_slice(self) -> &'a [Id] {
37 match self {
38 Self::Loaded(children) => children,
39 Self::Leaf | Self::Unloaded | Self::Loading => &[],
40 }
41 }
42
43 #[must_use]
45 pub const fn is_branch(self) -> bool {
46 !matches!(self, Self::Leaf)
47 }
48}
49
50#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
52pub enum TreeRootVisibility {
53 #[default]
55 Visible,
56 Hidden,
58}
59
60#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
62pub enum TreeSelectionFallback {
63 #[default]
65 ParentThenNearest,
66 Nearest,
68 Clear,
70}
71
72#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
74pub enum TreeFilterConfig {
75 #[default]
77 Disabled,
78 Enabled {
80 auto_expand: bool,
82 },
83}
84
85impl TreeFilterConfig {
86 #[must_use]
88 pub const fn enabled() -> Self {
89 Self::Enabled { auto_expand: true }
90 }
91
92 #[must_use]
94 pub const fn enabled_manual_expand() -> Self {
95 Self::Enabled { auto_expand: false }
96 }
97}
98
99#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
104pub struct TreeRevision(u64);
105
106impl TreeRevision {
107 pub const INITIAL: Self = Self(0);
109
110 #[must_use]
112 pub const fn new(value: u64) -> Self {
113 Self(value)
114 }
115
116 #[must_use]
118 pub const fn get(self) -> u64 {
119 self.0
120 }
121
122 #[must_use]
124 pub const fn next(self) -> Self {
125 Self(self.0.wrapping_add(1))
126 }
127
128 pub const fn advance(&mut self) {
130 *self = self.next();
131 }
132}
133
134impl From<u64> for TreeRevision {
135 fn from(value: u64) -> Self {
136 Self::new(value)
137 }
138}
139
140#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
142pub struct NoFilter;
143
144impl<T: TreeModel> TreeFilter<T> for NoFilter {
145 #[inline]
146 fn is_match(&self, _model: &T, _id: T::Id) -> bool {
147 true
148 }
149}
150
151#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
153pub struct NoSort;
154
155impl<T: TreeModel> TreeSort<T> for NoSort {
156 fn compare(&self, _model: &T, _left: T::Id, _right: T::Id) -> Ordering {
157 Ordering::Equal
158 }
159
160 fn is_enabled(&self) -> bool {
161 false
162 }
163}
164
165#[derive(Clone, Debug)]
167pub struct TreeQuery<F = NoFilter, S = NoSort> {
168 filter: QueryPolicy<F>,
169 sort: QueryPolicy<S>,
170 filter_config: TreeFilterConfig,
171 root_visibility: TreeRootVisibility,
172 selection_fallback: TreeSelectionFallback,
173}
174
175impl TreeQuery {
176 #[must_use]
178 pub const fn new() -> Self {
179 Self {
180 filter: QueryPolicy::new(NoFilter, TreeRevision::INITIAL),
181 sort: QueryPolicy::new(NoSort, TreeRevision::INITIAL),
182 filter_config: TreeFilterConfig::Disabled,
183 root_visibility: TreeRootVisibility::Visible,
184 selection_fallback: TreeSelectionFallback::ParentThenNearest,
185 }
186 }
187}
188
189impl<F, S> TreeQuery<F, S> {
190 #[must_use]
192 pub fn with_filter<NF>(
193 self,
194 filter: NF,
195 config: TreeFilterConfig,
196 revision: TreeRevision,
197 ) -> TreeQuery<NF, S> {
198 TreeQuery {
199 filter: QueryPolicy::replacement(filter, revision),
200 sort: self.sort,
201 filter_config: config,
202 root_visibility: self.root_visibility,
203 selection_fallback: self.selection_fallback,
204 }
205 }
206
207 #[must_use]
209 pub fn with_sort<NS>(self, sort: NS, revision: TreeRevision) -> TreeQuery<F, NS> {
210 TreeQuery {
211 filter: self.filter,
212 sort: QueryPolicy::replacement(sort, revision),
213 filter_config: self.filter_config,
214 root_visibility: self.root_visibility,
215 selection_fallback: self.selection_fallback,
216 }
217 }
218
219 #[must_use]
221 pub const fn with_filter_config(mut self, config: TreeFilterConfig) -> Self {
222 self.filter_config = config;
223 self
224 }
225
226 #[must_use]
228 pub const fn with_root_visibility(mut self, visibility: TreeRootVisibility) -> Self {
229 self.root_visibility = visibility;
230 self
231 }
232
233 #[must_use]
235 pub const fn with_selection_fallback(mut self, fallback: TreeSelectionFallback) -> Self {
236 self.selection_fallback = fallback;
237 self
238 }
239
240 #[must_use]
242 pub const fn filter(&self) -> &F {
243 &self.filter.value
244 }
245
246 pub const fn filter_mut(&mut self) -> &mut F {
248 self.filter.value_mut()
249 }
250
251 #[must_use]
253 pub const fn sort(&self) -> &S {
254 &self.sort.value
255 }
256
257 pub const fn sort_mut(&mut self) -> &mut S {
259 self.sort.value_mut()
260 }
261
262 pub const fn touch_filter(&mut self) {
264 self.filter.touch();
265 }
266
267 pub const fn touch_sort(&mut self) {
269 self.sort.touch();
270 }
271
272 pub fn set_filter_config(&mut self, config: TreeFilterConfig) -> bool {
274 if self.filter_config == config {
275 return false;
276 }
277 self.filter_config = config;
278 true
279 }
280
281 pub fn set_root_visibility(&mut self, visibility: TreeRootVisibility) -> bool {
283 let changed = self.root_visibility != visibility;
284 self.root_visibility = visibility;
285 changed
286 }
287
288 pub fn set_selection_fallback(&mut self, fallback: TreeSelectionFallback) -> bool {
290 let changed = self.selection_fallback != fallback;
291 self.selection_fallback = fallback;
292 changed
293 }
294
295 #[must_use]
297 pub const fn filter_config(&self) -> TreeFilterConfig {
298 self.filter_config
299 }
300
301 #[must_use]
303 pub const fn root_visibility(&self) -> TreeRootVisibility {
304 self.root_visibility
305 }
306
307 #[must_use]
309 pub const fn selection_fallback(&self) -> TreeSelectionFallback {
310 self.selection_fallback
311 }
312
313 #[must_use]
315 pub const fn filter_revision(&self) -> TreeRevision {
316 self.filter.revision
317 }
318
319 #[must_use]
321 pub const fn sort_revision(&self) -> TreeRevision {
322 self.sort.revision
323 }
324
325 pub(crate) const fn filter_generation(&self) -> TreeRevision {
326 self.filter.generation
327 }
328
329 pub(crate) const fn sort_generation(&self) -> TreeRevision {
330 self.sort.generation
331 }
332}
333
334impl Default for TreeQuery {
335 fn default() -> Self {
336 Self::new()
337 }
338}
339#[derive(Clone, Debug)]
340struct QueryPolicy<P> {
341 value: P,
342 revision: TreeRevision,
343 generation: TreeRevision,
344}
345
346impl<P> QueryPolicy<P> {
347 const fn new(value: P, revision: TreeRevision) -> Self {
348 Self {
349 value,
350 revision,
351 generation: TreeRevision::INITIAL,
352 }
353 }
354
355 fn replacement(value: P, revision: TreeRevision) -> Self {
356 Self {
357 value,
358 revision,
359 generation: next_query_policy_generation(),
360 }
361 }
362
363 const fn value_mut(&mut self) -> &mut P {
364 self.revision.advance();
365 &mut self.value
366 }
367
368 const fn touch(&mut self) {
369 self.revision.advance();
370 }
371}
372
373pub trait TreeModel {
380 type Id: Copy + Eq + Hash;
382
383 fn roots(&self) -> impl Iterator<Item = Self::Id> + '_;
385
386 fn children(&self, id: Self::Id) -> TreeChildren<'_, Self::Id>;
388
389 fn revision(&self) -> TreeRevision;
391
392 fn size_hint(&self) -> usize {
394 0
395 }
396}
397
398pub trait TreeFilter<T: TreeModel> {
400 fn is_match(&self, model: &T, id: T::Id) -> bool;
402}
403
404impl<T, F> TreeFilter<T> for F
405where
406 T: TreeModel,
407 F: Fn(&T, T::Id) -> bool,
408{
409 #[inline]
410 fn is_match(&self, model: &T, id: T::Id) -> bool {
411 self(model, id)
412 }
413}
414
415pub trait TreeSort<T: TreeModel> {
417 fn compare(&self, model: &T, left: T::Id, right: T::Id) -> Ordering;
419
420 fn is_enabled(&self) -> bool {
422 true
423 }
424}
425
426impl<T, F> TreeSort<T> for F
427where
428 T: TreeModel,
429 F: Fn(&T, T::Id, T::Id) -> Ordering,
430{
431 fn compare(&self, model: &T, left: T::Id, right: T::Id) -> Ordering {
432 self(model, left, right)
433 }
434}
435
436fn next_query_policy_generation() -> TreeRevision {
437 TreeRevision::new(NEXT_QUERY_POLICY_GENERATION.fetch_add(1, AtomicOrdering::Relaxed))
438}