Skip to main content

doublets/data/
traits.rs

1#[cfg(feature = "rayon")]
2use rayon::prelude::*;
3
4use std::collections::HashSet;
5
6use crate::{Error, Fuse, Link};
7use data::{Flow, LinkReference, LinksConstants, ToQuery};
8
9/// Callback type for read-only link enumeration.
10pub type ReadHandler<'a, T> = &'a mut dyn FnMut(Link<T>) -> Flow;
11
12/// Callback type for mutating operations that report `(before, after)` link states.
13pub type WriteHandler<'a, T> = &'a mut dyn FnMut(Link<T>, Link<T>) -> Flow;
14
15/// Low-level, raw slice-based interface for a doublets link store.
16///
17/// Implementors are required to be `Send + Sync`. Most users should prefer the
18/// higher-level [`Doublets`] trait which builds on this one.
19pub trait Links<T: LinkReference>: Send + Sync {
20    /// Returns the store's [`LinksConstants`] (any/null/range values).
21    fn constants(&self) -> &LinksConstants<T>;
22
23    /// Counts links that match `query`.
24    fn count_links(&self, query: &[T]) -> T;
25
26    /// Creates one or more links matching `query` and reports each creation via `handler`.
27    fn create_links(&mut self, query: &[T], handler: WriteHandler<'_, T>)
28        -> Result<Flow, Error<T>>;
29
30    /// Iterates over links matching `query`, calling `handler` for each.
31    fn each_links(&self, query: &[T], handler: ReadHandler<'_, T>) -> Flow;
32
33    /// Updates links matching `query` to the new values in `change`, reporting via `handler`.
34    fn update_links(
35        &mut self,
36        query: &[T],
37        change: &[T],
38        handler: WriteHandler<'_, T>,
39    ) -> Result<Flow, Error<T>>;
40
41    /// Deletes links matching `query` and reports each deletion via `handler`.
42    fn delete_links(&mut self, query: &[T], handler: WriteHandler<'_, T>)
43        -> Result<Flow, Error<T>>;
44}
45
46/// High-level API for a doublets link store, extending [`Links`] with ergonomic helpers.
47///
48/// All methods have default implementations built on top of [`Links`].
49pub trait Doublets<T: LinkReference>: Links<T> {
50    /// Counts links matching `query`.
51    fn count_by(&self, query: impl ToQuery<T>) -> T
52    where
53        Self: Sized,
54    {
55        self.count_links(&query.to_query()[..])
56    }
57
58    /// Returns the total number of links in the store.
59    fn count(&self) -> T
60    where
61        Self: Sized,
62    {
63        self.count_by([])
64    }
65
66    /// Creates a link matching `query`, calling `handler` on each created link.
67    fn create_by_with<F>(
68        &mut self,
69        query: impl ToQuery<T>,
70        mut handler: F,
71    ) -> Result<Flow, Error<T>>
72    where
73        F: FnMut(Link<T>, Link<T>) -> Flow,
74        Self: Sized,
75    {
76        let query = query.to_query();
77        self.create_links(&query[..], &mut handler)
78    }
79
80    /// Creates a link matching `query` and returns its index.
81    fn create_by(&mut self, query: impl ToQuery<T>) -> Result<T, Error<T>>
82    where
83        Self: Sized,
84    {
85        let mut index = T::from_byte(0);
86        self.create_by_with(query, |_before, link| {
87            index = link.index;
88            Flow::Continue
89        })
90        .map(|_| index)
91    }
92
93    /// Creates a new link and calls `handler` with the before/after states.
94    fn create_with<F>(&mut self, handler: F) -> Result<Flow, Error<T>>
95    where
96        F: FnMut(Link<T>, Link<T>) -> Flow,
97        Self: Sized,
98    {
99        self.create_by_with([], handler)
100    }
101
102    /// Creates a new uninitialized link and returns its index.
103    fn create(&mut self) -> Result<T, Error<T>>
104    where
105        Self: Sized,
106    {
107        self.create_by([])
108    }
109
110    /// Iterates over links matching `query`, calling `handler` for each.
111    fn each_by<F>(&self, query: impl ToQuery<T>, mut handler: F) -> Flow
112    where
113        F: FnMut(Link<T>) -> Flow,
114        Self: Sized,
115    {
116        let query = query.to_query();
117        self.each_links(&query[..], &mut handler)
118    }
119
120    /// Iterates over all links in the store, calling `handler` for each.
121    fn each<F>(&self, handler: F) -> Flow
122    where
123        F: FnMut(Link<T>) -> Flow,
124        Self: Sized,
125    {
126        self.each_by([], handler)
127    }
128
129    /// Updates links matching `query` to `change`, calling `handler` with before/after.
130    fn update_by_with<H>(
131        &mut self,
132        query: impl ToQuery<T>,
133        change: impl ToQuery<T>,
134        mut handler: H,
135    ) -> Result<Flow, Error<T>>
136    where
137        H: FnMut(Link<T>, Link<T>) -> Flow,
138        Self: Sized,
139    {
140        let query = query.to_query();
141        let change = change.to_query();
142        self.update_links(&query[..], &change[..], &mut handler)
143    }
144
145    /// Updates links matching `query` to `change` and returns the updated link's index.
146    fn update_by(&mut self, query: impl ToQuery<T>, change: impl ToQuery<T>) -> Result<T, Error<T>>
147    where
148        Self: Sized,
149    {
150        let mut result = T::from_byte(0);
151        self.update_by_with(query, change, |_, after| {
152            result = after.index;
153            Flow::Continue
154        })
155        .map(|_| result)
156    }
157
158    /// Updates the link at `index` to `(index, source, target)`, calling `handler`.
159    fn update_with<F>(
160        &mut self,
161        index: T,
162        source: T,
163        target: T,
164        handler: F,
165    ) -> Result<Flow, Error<T>>
166    where
167        F: FnMut(Link<T>, Link<T>) -> Flow,
168        Self: Sized,
169    {
170        self.update_by_with([index], [index, source, target], handler)
171    }
172
173    /// Updates the link at `index` to `(index, source, target)` and returns the index.
174    fn update(&mut self, index: T, source: T, target: T) -> Result<T, Error<T>>
175    where
176        Self: Sized,
177    {
178        self.update_by([index], [index, source, target])
179    }
180
181    /// Deletes links matching `query`, calling `handler` with before/after states.
182    fn delete_by_with<F>(
183        &mut self,
184        query: impl ToQuery<T>,
185        mut handler: F,
186    ) -> Result<Flow, Error<T>>
187    where
188        F: FnMut(Link<T>, Link<T>) -> Flow,
189        Self: Sized,
190    {
191        let query = query.to_query();
192        self.delete_links(&query[..], &mut handler)
193    }
194
195    /// Deletes links matching `query` and returns the deleted link's index.
196    fn delete_by(&mut self, query: impl ToQuery<T>) -> Result<T, Error<T>>
197    where
198        Self: Sized,
199    {
200        let mut result = T::from_byte(0);
201        self.delete_by_with(query, |_before, after| {
202            result = after.index;
203            Flow::Continue
204        })
205        .map(|_| result)
206    }
207
208    /// Deletes the link at `index`, calling `handler` with before/after states.
209    fn delete_with<F>(&mut self, index: T, handler: F) -> Result<Flow, Error<T>>
210    where
211        F: FnMut(Link<T>, Link<T>) -> Flow,
212        Self: Sized,
213    {
214        self.delete_by_with([index], handler)
215    }
216
217    /// Deletes the link at `index` and returns its former index.
218    fn delete(&mut self, index: T) -> Result<T, Error<T>>
219    where
220        Self: Sized,
221    {
222        self.delete_by([index])
223    }
224
225    /// Returns the link at `index`, or `Err(Error::NotExists)` if it does not exist.
226    fn try_get_link(&self, index: T) -> Result<Link<T>, Error<T>> {
227        self.get_link(index).ok_or(Error::NotExists(index))
228    }
229
230    /// Returns the link at `index`, or `None` if it does not exist.
231    fn get_link(&self, index: T) -> Option<Link<T>>;
232
233    /// Deletes all links in the store.
234    fn delete_all(&mut self) -> Result<(), Error<T>>
235    where
236        Self: Sized,
237    {
238        let mut count = self.count();
239        while count != T::from_byte(0) {
240            self.delete(count)?;
241            count = self.count();
242        }
243        Ok(())
244    }
245
246    /// Deletes all links matching `query`, calling `handler` for each deletion.
247    /// Duplicate matches are deleted once, in reverse order of their first occurrence.
248    fn delete_query_with<F>(&mut self, query: impl ToQuery<T>, handler: F) -> Result<(), Error<T>>
249    where
250        F: FnMut(Link<T>, Link<T>) -> Flow,
251        Self: Sized,
252    {
253        let query = query.to_query();
254        let len = self.count_by(query.to_query()).as_();
255        let mut vec = Vec::with_capacity(len);
256        let mut seen = HashSet::with_capacity(len);
257
258        self.each_by(query, |link| {
259            if seen.insert(link.index) {
260                vec.push(link.index);
261            }
262            Flow::Continue
263        });
264
265        let mut handler = Fuse::new(handler);
266        for index in vec.into_iter().rev() {
267            self.delete_links(&[index], &mut |before, after| handler.call(before, after))?;
268        }
269        Ok(())
270    }
271
272    /// Deletes each other link that uses `index` as a source or target once,
273    /// calling `handler` for each deletion.
274    fn delete_usages_with<F>(&mut self, index: T, handler: F) -> Result<(), Error<T>>
275    where
276        F: FnMut(Link<T>, Link<T>) -> Flow,
277        Self: Sized,
278    {
279        let any = self.constants().any;
280        let mut to_delete = Vec::with_capacity(
281            self.count_by([any, index, any]).as_() + self.count_by([any, any, index]).as_(),
282        );
283        self.each_by([any, index, any], |link| {
284            if link.index != index {
285                to_delete.push(link.index);
286            }
287            Flow::Continue
288        });
289
290        self.each_by([any, any, index], |link| {
291            if link.index != index && link.source != index {
292                to_delete.push(link.index);
293            }
294            Flow::Continue
295        });
296
297        let mut handler = Fuse::new(handler);
298        for index in to_delete.into_iter().rev() {
299            self.delete_links(&[index], &mut |before, after| handler.call(before, after))?;
300        }
301        Ok(())
302    }
303
304    /// Deletes all links that use `index` as a source or target.
305    fn delete_usages(&mut self, index: T) -> Result<(), Error<T>>
306    where
307        Self: Sized,
308    {
309        self.delete_usages_with(index, |_, _| Flow::Continue)
310    }
311
312    /// Creates a self-referential point link and returns its index.
313    fn create_point(&mut self) -> Result<T, Error<T>>
314    where
315        Self: Sized,
316    {
317        let new = self.create()?;
318        self.update(new, new, new)
319    }
320
321    /// Creates a link from `source` to `target`, calling `handler` with before/after states.
322    fn create_link_with<F>(&mut self, source: T, target: T, handler: F) -> Result<Flow, Error<T>>
323    where
324        F: FnMut(Link<T>, Link<T>) -> Flow,
325        Self: Sized,
326    {
327        let mut new = T::from_byte(0);
328        let mut handler = Fuse::new(handler);
329        self.create_with(|before, after| {
330            new = after.index;
331            handler.call(before, after);
332            Flow::Continue
333        })?;
334
335        self.update_links(&[new], &[new, source, target], &mut |before, after| {
336            handler.call(before, after)
337        })
338    }
339
340    /// Creates a link from `source` to `target` and returns its index.
341    fn create_link(&mut self, source: T, target: T) -> Result<T, Error<T>>
342    where
343        Self: Sized,
344    {
345        let mut result = T::from_byte(0);
346        self.create_link_with(source, target, |_, link| {
347            result = link.index;
348            Flow::Continue
349        })
350        .map(|_| result)
351    }
352
353    /// Returns `true` if at least one link matches `query`.
354    fn found(&self, query: impl ToQuery<T>) -> bool
355    where
356        Self: Sized,
357    {
358        self.count_by(query) != T::from_byte(0)
359    }
360
361    /// Returns the first link matching `query`, or `None`.
362    fn find(&self, query: impl ToQuery<T>) -> Option<Link<T>>
363    where
364        Self: Sized,
365    {
366        let mut result = None;
367        self.each_by(query, |link| {
368            result = Some(link);
369            Flow::Break
370        });
371        result
372    }
373
374    /// Returns the index of a link with the given `source` and `target`, or `None`.
375    fn search(&self, source: T, target: T) -> Option<T>
376    where
377        Self: Sized,
378    {
379        self.find([self.constants().any, source, target])
380            .map(|link| link.index)
381    }
382
383    #[deprecated(note = "use `search` instead")]
384    fn search_or(&self, source: T, target: T, default: T) -> T
385    where
386        Self: Sized,
387    {
388        self.search(source, target).unwrap_or(default)
389    }
390
391    /// Returns the link matching `query` only if exactly one link matches; `None` otherwise.
392    fn single(&self, query: impl ToQuery<T>) -> Option<Link<T>>
393    where
394        Self: Sized,
395    {
396        let mut result = None;
397        self.each_by(query, |link| {
398            if result.is_none() {
399                result = Some(link);
400                Flow::Continue
401            } else {
402                result = None;
403                Flow::Break
404            }
405        });
406        result
407    }
408
409    /// Returns the index of the `(source, target)` link, creating it if it does not exist.
410    fn get_or_create(&mut self, source: T, target: T) -> Result<T, Error<T>>
411    where
412        Self: Sized,
413    {
414        if let Some(link) = self.search(source, target) {
415            Ok(link)
416        } else {
417            self.create_link(source, target)
418        }
419    }
420
421    /// Returns the number of distinct other links that reference `index` as a source or target.
422    fn count_usages(&self, index: T) -> Result<T, Error<T>>
423    where
424        Self: Sized,
425    {
426        let any = self.constants().any;
427
428        let link = self.try_get_link(index)?;
429
430        let usage_source = self.count_by([any, index, any]);
431        let usage_target = self.count_by([any, any, index]);
432        let usage_both = self.count_by([any, index, index]);
433        // Remove the overlap before adding to avoid overflowing narrow address types.
434        let mut usages = usage_source + (usage_target - usage_both);
435        if index == link.source || index == link.target {
436            usages = usages - T::from_byte(1);
437        }
438
439        Ok(usages)
440    }
441
442    /// Returns the indices of distinct other links that reference `index` as a source or target.
443    fn usages(&self, index: T) -> Result<Vec<T>, Error<T>>
444    where
445        Self: Sized,
446    {
447        let any = self.constants().any;
448        let mut usages = Vec::with_capacity(self.count_usages(index)?.as_());
449
450        self.each_by([any, index, any], |link| {
451            if link.index != index {
452                usages.push(link.index);
453            }
454            Flow::Continue
455        });
456
457        self.each_by([any, any, index], |link| {
458            if link.index != index && link.source != index {
459                usages.push(link.index);
460            }
461            Flow::Continue
462        });
463        Ok(usages)
464    }
465
466    /// Returns `true` if the link at `link` exists (internal or external).
467    fn exist(&self, link: T) -> bool
468    where
469        Self: Sized,
470    {
471        let constants = self.constants();
472        if constants.is_external(link) {
473            true
474        } else {
475            constants.is_internal(link) && self.count_by([link]) != T::from_byte(0)
476        }
477    }
478
479    /// Returns `true` if any other link references `link` as a source or target.
480    fn has_usages(&self, link: T) -> bool
481    where
482        Self: Sized,
483    {
484        self.count_usages(link)
485            .is_ok_and(|link| link != T::from_byte(0))
486    }
487
488    /// Re-points all usages of `old` to `new`, calling `handler` for each update.
489    ///
490    /// A usage that references `old` as both source and target is updated once,
491    /// replacing both references together. The link at `old` itself is excluded.
492    fn rebase_with<F>(&mut self, old: T, new: T, handler: F) -> Result<(), Error<T>>
493    where
494        F: FnMut(Link<T>, Link<T>) -> Flow,
495        Self: Sized,
496    {
497        let _ = self.try_get_link(old)?;
498
499        if old == new {
500            return Ok(());
501        }
502
503        let any = self.constants().any;
504
505        let mut handler = Fuse::new(handler);
506
507        let usages: Vec<_> = self
508            .each_iter([any, old, any])
509            // Usages with `old` on both sides were already found by the source query.
510            .chain(
511                self.each_iter([any, any, old])
512                    .filter(|usage| usage.source != old),
513            )
514            .filter(|usage| usage.index != old)
515            .collect();
516        for usage in usages {
517            let source = if usage.source == old {
518                new
519            } else {
520                usage.source
521            };
522            let target = if usage.target == old {
523                new
524            } else {
525                usage.target
526            };
527            self.update_links(
528                &[usage.index],
529                &[usage.index, source, target],
530                &mut |before, after| handler.call(before, after),
531            )?;
532        }
533        Ok(())
534    }
535
536    /// Re-points all usages of `old` to `new` and returns `new`.
537    fn rebase(&mut self, old: T, new: T) -> Result<T, Error<T>>
538    where
539        Self: Sized,
540    {
541        self.rebase_with(old, new, |_, _| Flow::Continue)
542            .map(|()| new)
543    }
544
545    /// Re-points all usages of `old` to `new`, then deletes `old`. Returns `new`.
546    fn rebase_and_delete(&mut self, old: T, new: T) -> Result<T, Error<T>>
547    where
548        Self: Sized,
549    {
550        if old == new {
551            Ok(new)
552        } else {
553            self.rebase(old, new)?;
554            self.delete(old)
555        }
556    }
557}
558
559impl<T: LinkReference, All: Doublets<T> + ?Sized> Links<T> for Box<All> {
560    fn constants(&self) -> &LinksConstants<T> {
561        (**self).constants()
562    }
563
564    fn count_links(&self, query: &[T]) -> T {
565        (**self).count_links(query)
566    }
567
568    fn create_links(
569        &mut self,
570        query: &[T],
571        handler: WriteHandler<'_, T>,
572    ) -> Result<Flow, Error<T>> {
573        (**self).create_links(query, handler)
574    }
575
576    fn each_links(&self, query: &[T], handler: ReadHandler<'_, T>) -> Flow {
577        (**self).each_links(query, handler)
578    }
579
580    fn update_links(
581        &mut self,
582        query: &[T],
583        change: &[T],
584        handler: WriteHandler<'_, T>,
585    ) -> Result<Flow, Error<T>> {
586        (**self).update_links(query, change, handler)
587    }
588
589    fn delete_links(
590        &mut self,
591        query: &[T],
592        handler: WriteHandler<'_, T>,
593    ) -> Result<Flow, Error<T>> {
594        (**self).delete_links(query, handler)
595    }
596}
597
598impl<T: LinkReference, All: Doublets<T> + ?Sized> Doublets<T> for Box<All> {
599    fn get_link(&self, index: T) -> Option<Link<T>> {
600        (**self).get_link(index)
601    }
602}
603
604/// Extension trait that adds iterator-based access to a [`Doublets`] store.
605///
606/// Automatically implemented for any type that implements [`Doublets`].
607pub trait DoubletsExt<T: LinkReference>: Sized + Doublets<T> {
608    /// The parallel iterator type returned by [`par_iter`](DoubletsExt::par_iter).
609    #[cfg(feature = "rayon")]
610    type IdxParIter: IndexedParallelIterator<Item = Link<T>>;
611
612    /// Returns a parallel iterator over all links in the store.
613    #[cfg(feature = "rayon")]
614    fn par_iter(&self) -> Self::IdxParIter;
615
616    /// Returns a parallel iterator over links matching `query`.
617    #[cfg(feature = "rayon")]
618    fn par_each_iter(&self, query: impl ToQuery<T>) -> Self::IdxParIter;
619
620    /// Returns an iterator over all links in the store.
621    fn iter(&self) -> impl Iterator<Item = Link<T>> + ExactSizeIterator + DoubleEndedIterator;
622
623    /// Returns an iterator over links matching `query`.
624    fn each_iter(
625        &self,
626        query: impl ToQuery<T>,
627    ) -> impl Iterator<Item = Link<T>> + ExactSizeIterator + DoubleEndedIterator;
628
629    /// Returns a small-vec-backed iterator over all links (optimised for small result sets).
630    #[cfg(feature = "small-search")]
631    fn iter_small(&self)
632        -> impl Iterator<Item = Link<T>> + ExactSizeIterator + DoubleEndedIterator;
633
634    /// Returns a small-vec-backed iterator over links matching `query`.
635    #[cfg(feature = "small-search")]
636    fn each_iter_small(
637        &self,
638        query: impl ToQuery<T>,
639    ) -> impl Iterator<Item = Link<T>> + ExactSizeIterator + DoubleEndedIterator;
640}
641
642impl<T: LinkReference, All: Doublets<T> + Sized> DoubletsExt<T> for All {
643    #[cfg(feature = "rayon")]
644    type IdxParIter = rayon::vec::IntoIter<Link<T>>;
645
646    #[cfg(feature = "rayon")]
647    fn par_iter(&self) -> Self::IdxParIter {
648        self.par_each_iter([self.constants().any; 3])
649    }
650
651    #[cfg(feature = "rayon")]
652    fn par_each_iter(&self, query: impl ToQuery<T>) -> Self::IdxParIter {
653        let mut vec = Vec::with_capacity(self.count_by(query.to_query()).as_());
654        self.each_by(query, |link| {
655            vec.push(link);
656            Flow::Continue
657        });
658        vec.into_par_iter()
659    }
660
661    #[inline]
662    fn iter(&self) -> impl Iterator<Item = Link<T>> + ExactSizeIterator + DoubleEndedIterator {
663        self.each_iter([self.constants().any; 3])
664    }
665
666    #[cfg_attr(feature = "more-inline", inline)]
667    fn each_iter(
668        &self,
669        query: impl ToQuery<T>,
670    ) -> impl Iterator<Item = Link<T>> + ExactSizeIterator + DoubleEndedIterator {
671        let cap = self.count_by(query.to_query()).as_();
672
673        let mut vec = Vec::with_capacity(cap);
674        self.each_by(query, &mut |link| {
675            vec.push(link);
676            Flow::Continue
677        });
678        vec.into_iter()
679    }
680
681    #[inline]
682    #[cfg(feature = "small-search")]
683    fn iter_small(
684        &self,
685    ) -> impl Iterator<Item = Link<T>> + ExactSizeIterator + DoubleEndedIterator {
686        self.each_iter_small([self.constants().any; 3])
687    }
688
689    #[cfg(feature = "small-search")]
690    #[cfg_attr(feature = "more-inline", inline)]
691    fn each_iter_small(
692        &self,
693        query: impl ToQuery<T>,
694    ) -> impl Iterator<Item = Link<T>> + ExactSizeIterator + DoubleEndedIterator {
695        const SIZE_HINT: usize = 2;
696
697        let mut vec = smallvec::SmallVec::<[Link<_>; SIZE_HINT]>::with_capacity(
698            self.count_by(query.to_query()).as_(),
699        );
700        self.each_by(query, |link| {
701            vec.push(link);
702            Flow::Continue
703        });
704        vec.into_iter()
705    }
706}