stam/
api.rs

1/*
2    STAM Library (Stand-off Text Annotation Model)
3        by Maarten van Gompel <proycon@anaproy.nl>
4        Digital Infrastucture, KNAW Humanities Cluster
5
6        Licensed under the GNU General Public License v3
7
8        https://github.com/annotation/stam-rust
9*/
10
11//! This is the root module for the high-level API. See the different submodules for further
12//! documentation regarding each STAM object.
13
14mod annotation;
15mod annotationdata;
16mod annotationdataset;
17mod annotationstore;
18mod datakey;
19mod query;
20mod resources;
21mod substore;
22mod text;
23mod textselection;
24
25#[cfg(feature = "webanno")]
26mod webanno;
27
28#[cfg(feature = "transpose")]
29mod transpose;
30
31#[cfg(feature = "translate")]
32mod translate;
33
34pub use annotation::*;
35pub use annotationdata::*;
36pub use annotationdataset::*;
37pub use datakey::*;
38pub use query::*;
39pub use resources::*;
40pub use text::*;
41pub use textselection::*;
42
43#[cfg(feature = "webanno")]
44pub use webanno::*;
45
46#[cfg(feature = "transpose")]
47pub use transpose::*;
48
49#[cfg(feature = "translate")]
50pub use translate::*;
51
52#[cfg(feature = "textvalidation")]
53pub use crate::textvalidation::*;
54
55use crate::annotation::{Annotation, AnnotationHandle};
56use crate::annotationdata::{AnnotationData, AnnotationDataHandle};
57use crate::annotationdataset::{AnnotationDataSet, AnnotationDataSetHandle};
58use crate::annotationstore::AnnotationStore;
59use crate::datakey::{DataKey, DataKeyHandle};
60use crate::datavalue::{DataOperator, DataValue};
61use crate::resources::{TextResource, TextResourceHandle};
62use crate::substore::AnnotationSubStoreHandle;
63use crate::textselection::{TextSelection, TextSelectionOperator};
64
65use crate::{store::*, Offset, TextSelectionHandle};
66
67use regex::Regex;
68use std::borrow::Cow;
69use std::collections::VecDeque;
70use std::fmt::Debug;
71use std::marker::PhantomData;
72
73/// Holds a collection of items. The collection may be either
74/// owned or borrowed from the store (usually from a reverse index).
75///
76/// The items in the collection by definition refer to the [`AnnotationStore`], as
77/// internally the collection only keeps *fully qualified* handles and a reference to the store.
78///
79/// This structure is produced via the [`ToHandles`] trait that is implemented
80/// for all iterators over [`ResultItem<T>`].
81#[derive(Clone)]
82pub struct Handles<'store, T>
83where
84    T: Storable,
85{
86    array: Cow<'store, [T::FullHandleType]>,
87    /// Sorted by handle? (i.e. chronologically)
88    sorted: bool,
89    store: &'store AnnotationStore,
90}
91
92impl<'store, T> Debug for Handles<'store, T>
93where
94    T: Storable,
95{
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        let s = format!("Collection<{}>", T::typeinfo());
98        f.debug_struct(s.as_str())
99            .field("array", &self.array)
100            .field("sorted", &self.sorted)
101            .finish()
102    }
103}
104
105/// Iterator over the handles in a [`Handles<T>`] collection.
106pub type HandlesIter<'a, T> =
107    std::iter::Copied<std::slice::Iter<'a, <T as Storable>::FullHandleType>>;
108
109impl<'store, T> Handles<'store, T>
110where
111    T: Storable,
112{
113    /// Are the items in this collection sorted (chronologically, i.e. by handle) or not?
114    pub fn returns_sorted(&self) -> bool {
115        self.sorted
116    }
117
118    /// Returns a reference to the underlying [`AnnotationStore`].
119    pub fn store(&self) -> &'store AnnotationStore {
120        self.store
121    }
122
123    /// Low-level method to instantiate annotations from an existing vector of handles (either owned or borrowed from the store).
124    /// Warning: Use of this function is dangerous and discouraged in most cases as there is no validity check on the handles you pass!
125    ///
126    /// The safe option is to convert from an iterator of [`ResultItem<T>`] to [`Handles<T>`], then use [`ToHandles::to_handles()`] on that
127    pub fn new(
128        array: Cow<'store, [T::FullHandleType]>,
129        sorted: bool,
130        store: &'store AnnotationStore,
131    ) -> Self {
132        Self {
133            array,
134            sorted,
135            store,
136        }
137    }
138
139    /// Returns a new empty handles collection
140    pub fn new_empty(store: &'store AnnotationStore) -> Self {
141        Self {
142            array: Cow::Owned(Vec::new()),
143            sorted: false,
144            store,
145        }
146    }
147
148    /// Create a new handles collection from an iterator handles
149    /// Warning: Use of this function is dangerous and discouraged in most cases as there is no validity check on the handles you pass!
150    ///
151    /// If you want to convert from an iterator of [`ResultItem<T>`] to [`Handles<T>`], then use [`ToHandles::to_handles()`] on that
152    pub fn from_iter(
153        iter: impl Iterator<Item = T::FullHandleType>,
154        store: &'store AnnotationStore,
155    ) -> Self {
156        let mut sorted = true;
157        let mut v = Vec::new();
158        let mut prev: Option<T::FullHandleType> = None;
159        for item in iter {
160            if let Some(p) = prev {
161                if p > item {
162                    sorted = false;
163                }
164            }
165            v.push(item);
166            prev = Some(item);
167        }
168        Self {
169            array: Cow::Owned(v),
170            sorted,
171            store,
172        }
173    }
174
175    /// Low-level method to take out the underlying vector of handles
176    pub fn take(self) -> Cow<'store, [T::FullHandleType]>
177    where
178        Self: Sized,
179    {
180        self.array
181    }
182
183    /// Returns the number of items in this collection.
184    pub fn len(&self) -> usize {
185        self.array.len()
186    }
187
188    /// Returns the number of items in this collection.
189    pub fn get(&self, index: usize) -> Option<T::FullHandleType> {
190        self.array.get(index).copied()
191    }
192
193    /// Returns a boolean indicating whether the collection is empty or not.
194    pub fn is_empty(&self) -> bool {
195        self.array.is_empty()
196    }
197
198    /// Tests if the collection contains a specific element
199    pub fn contains(&self, handle: &T::FullHandleType) -> bool {
200        if self.sorted {
201            match self.array.binary_search(&handle) {
202                Ok(_) => true,
203                Err(_) => false,
204            }
205        } else {
206            self.array.contains(&handle)
207        }
208    }
209
210    /// Tests if the collection contains a specific element and returns the index
211    pub fn position(&self, handle: &T::FullHandleType) -> Option<usize> {
212        if self.sorted {
213            match self.array.binary_search(&handle) {
214                Ok(index) => Some(index),
215                Err(_) => None,
216            }
217        } else {
218            self.array.iter().position(|x| x == handle)
219        }
220    }
221
222    /// Returns an iterator over the low-level handles in this collection
223    /// If you want to iterate over the actual items ([`ResultItem<T>`]), then use [`Self::items()`] instead.
224    pub fn iter<'a>(&'a self) -> HandlesIter<'a, T> {
225        self.array.iter().copied()
226    }
227
228    /// Returns an iterator over the high-level items in this collection
229    /// If you want to iterate over the low-level handles, then use [`Self::iter()`] instead.
230    /// If you want to consume the current iterator, use `into_items()` instead.
231    pub fn items<'a>(&'a self) -> FromHandles<'store, T, HandlesIter<'store, T>>
232    where
233        'a: 'store,
234    {
235        FromHandles::new(self.iter(), self.store())
236    }
237
238    /// Returns an iterator over the high-level items in this collection
239    /// If you want to iterate over the low-level handles, then use [`Self::iter()`] instead.
240    /// If you do not want to consume the current iterator, use `items()` instead.
241    pub fn into_items(self) -> FromHandles<'store, T, OwnedHandlesIter<'store, T>> {
242        let store = self.store();
243        FromHandles::new(self.into_iter(), store)
244    }
245
246    /// Computes the union between two collections, retains order and ensures there are no duplicates
247    /// Modifies the collection in-place to accommodate the other
248    pub fn union(&mut self, other: &Self) {
249        match other.len() {
250            0 => return,                                 //edge-case
251            1 => self.add(other.iter().next().unwrap()), //edge-case
252            _ => {
253                let mut updated = false;
254                let mut offset = 0;
255                for item in other.iter() {
256                    if self.sorted && other.sorted {
257                        //optimisation if both are sorted
258                        match self.array[offset..].binary_search(&item) {
259                            Ok(index) => offset = index + 1,
260                            Err(index) => {
261                                offset = index + 1;
262                                updated = true;
263                                self.add_unchecked(item);
264                            }
265                        }
266                    } else {
267                        if !self.contains(&item) {
268                            //will do either binary or linear search
269                            updated = true;
270                            self.add_unchecked(item);
271                        }
272                    }
273                }
274                if self.sorted && updated {
275                    //resort
276                    self.array.to_mut().sort_unstable();
277                }
278            }
279        }
280    }
281
282    /// Computes the intersection between two collections, retains order
283    /// Modifies the collection in-place to match the other
284    pub fn intersection(&mut self, other: &Self) {
285        match (self.len(), other.len()) {
286            (0, _) | (_, 0) =>
287            //edge-case: empty result because one of the collections is empty
288            {
289                self.array.to_mut().clear();
290                return;
291            }
292            (len, otherlen) => {
293                if len == otherlen && self.sorted && other.sorted {
294                    //edge-case: number of elements are equal, check if all elements are equal
295                    if self.iter().zip(other.iter()).all(|(x, y)| x == y) {
296                        return;
297                    }
298                } else if otherlen < len {
299                    //check if we need to modify the vector in place or if we can just copy the other
300                    if self.contains_subset(other) {
301                        self.array = other.array.clone(); //may be cheap if borrowed, expensive if owned
302                        return;
303                    }
304                } else if len < otherlen {
305                    //check if we need to modify the vector in place or if we can just copy the other
306                    if other.contains_subset(self) {
307                        return; //nothing to do
308                    }
309                }
310            }
311        }
312        let mut offset = 0;
313        // this takes ownership and will clone the array if it was borrowed
314        self.array.to_mut().retain(|x| {
315            if self.sorted && other.sorted {
316                //optimisation if both are sorted
317                match other.array[offset..].binary_search(x) {
318                    Ok(index) => {
319                        offset = index + 1;
320                        true
321                    }
322                    Err(index) => {
323                        offset = index + 1;
324                        false
325                    }
326                }
327            } else {
328                other.contains(x) //will do either binary or linear search
329            }
330        });
331    }
332
333    /// Checks if the collections contains another (need not be contingent)
334    pub fn contains_subset(&self, subset: &Self) -> bool {
335        for handle in subset.iter() {
336            if !self.contains(&handle) {
337                return false;
338            }
339        }
340        true
341    }
342
343    /// Sorts the collection in chronological order (i.e. the handles are sorted)
344    /// Note that this is *NOT* the same as textual order.
345    pub fn sort(&mut self) {
346        if !self.sorted {
347            self.array.to_mut().sort_unstable();
348            self.sorted = true;
349        }
350    }
351
352    /// Adds an item to the collection, this does *NOT* check for duplicates and MAY invalidate any existing sorting
353    pub(crate) fn add_unchecked(&mut self, item: T::FullHandleType) {
354        self.array.to_mut().push(item);
355    }
356
357    /// Adds an item to the collection, this checks for duplicates and respects existing sorting (if any),
358    /// so this comes with performance overhead. Use [`Self.union()`] to add multiple items at once more efficiently.
359    pub fn add(&mut self, item: T::FullHandleType) {
360        if self.sorted {
361            if let Err(pos) = self.array.binary_search(&item) {
362                self.array.to_mut().insert(pos, item)
363            }
364        } else {
365            if !self.contains(&item) {
366                self.array.to_mut().push(item);
367            }
368        }
369    }
370}
371
372pub struct OwnedHandlesIter<'store, T>
373where
374    T: Storable,
375{
376    handles: Handles<'store, T>,
377    cursor: usize,
378}
379
380impl<'store, T> Iterator for OwnedHandlesIter<'store, T>
381where
382    T: Storable,
383{
384    type Item = T::FullHandleType;
385    fn next(&mut self) -> Option<Self::Item> {
386        if let Some(handle) = self.handles.get(self.cursor) {
387            self.cursor += 1;
388            Some(handle)
389        } else {
390            None
391        }
392    }
393}
394
395impl<'store, T> IntoIterator for Handles<'store, T>
396where
397    T: Storable,
398{
399    type Item = T::FullHandleType;
400    type IntoIter = OwnedHandlesIter<'store, T>;
401    fn into_iter(self) -> Self::IntoIter {
402        OwnedHandlesIter {
403            handles: self,
404            cursor: 0,
405        }
406    }
407}
408
409/// This internal trait is implemented for various forms of [`FromHandles<'store,T>`]
410pub(crate) trait FullHandleToResultItem<'store, T>
411where
412    T: Storable,
413{
414    fn get_item(&self, handle: T::FullHandleType) -> Option<ResultItem<'store, T>>;
415}
416
417/// Iterator that turns iterators over full handles into [`ResultItem<T>`], holds a reference to the [`AnnotationStore`]
418pub struct FromHandles<'store, T, I>
419where
420    T: Storable + 'store,
421    I: Iterator<Item = T::FullHandleType>,
422{
423    inner: I,
424    store: &'store AnnotationStore,
425    _marker: PhantomData<T>, //zero-size, only needed to bind generic T
426}
427
428impl<'store, T, I> FromHandles<'store, T, I>
429where
430    T: Storable + 'store,
431    I: Iterator<Item = T::FullHandleType>,
432{
433    pub fn new(inner: I, store: &'store AnnotationStore) -> Self {
434        Self {
435            inner,
436            store,
437            _marker: PhantomData,
438        }
439    }
440}
441
442impl<'store, T, I> Iterator for FromHandles<'store, T, I>
443where
444    T: Storable + 'store,
445    I: Iterator<Item = T::FullHandleType>,
446    Self: FullHandleToResultItem<'store, T>,
447{
448    type Item = ResultItem<'store, T>;
449
450    #[inline]
451    fn next(&mut self) -> Option<Self::Item> {
452        loop {
453            if let Some(full_handle) = self.inner.next() {
454                let item = self.get_item(full_handle);
455                if item.is_none() {
456                    continue; //invalid handles are ignored
457                } else {
458                    return item;
459                }
460            } else {
461                return None;
462            }
463        }
464    }
465}
466
467/// This trait is implemented on iterators over [`ResultItem<T>`] and turns effectively collects
468/// these items, by only their handles and a reference to a store, as [`Handles<T>`].
469/// It is implemented alongside traits like [`AnnotationIterator`], [`DataIterator`], etc...
470pub trait ToHandles<'store, T>
471where
472    T: Storable,
473{
474    /// Convert an iterator over [`ResultItem<T>`] to [`Handles<T>`].
475    fn to_handles(&mut self, store: &'store AnnotationStore) -> Handles<'store, T>;
476}
477
478impl<'store, T, I> ToHandles<'store, T> for I
479where
480    T: Storable + 'store,
481    I: Iterator<Item = ResultItem<'store, T>>,
482    ResultItem<'store, T>: FullHandle<T>,
483{
484    fn to_handles(&mut self, store: &'store AnnotationStore) -> Handles<'store, T> {
485        Handles::from_iter(self.map(|item| item.fullhandle()), store)
486    }
487}
488
489/// This iterator implements a simple `.test()` method that just checks whether an iterator is
490/// empty or yields results. It is implemented alongside traits like [`AnnotationIterator`],
491/// [`DataIterator`], etc...
492pub trait TestableIterator: Iterator
493where
494    Self: Sized,
495{
496    /// Returns true if the iterator has items, false otherwise
497    fn test(mut self) -> bool {
498        self.next().is_some()
499    }
500}
501
502impl<I> TestableIterator for I where I: Iterator {} //blanket implementation
503
504/// An iterator that may be sorted or not and knows a-priori whether it is or not.
505pub trait MaybeSortedIterator: Iterator {
506    /// Does this iterator return items in sorted order?
507    fn returns_sorted(&self) -> bool;
508}
509
510/// An iterator that grabs the first available data value
511pub trait IteratorToValue<'a>: Iterator {
512    /// Grab the first available data value
513    fn value(&mut self) -> Option<&'a DataValue>;
514
515    /// Grab the first available data value as string, only works if it is actually a string. Does not work for numerals!
516    fn value_as_str(&mut self) -> Option<&'a str>;
517}
518
519/// An iterator that may be sorted or not and knows a-priori whether it is or not, it may also be a completely empty iterator.
520pub struct ResultIter<I>
521where
522    I: Iterator,
523{
524    inner: Option<I>,
525    sorted: bool,
526}
527
528impl<I: Iterator> MaybeSortedIterator for ResultIter<I> {
529    fn returns_sorted(&self) -> bool {
530        self.sorted
531    }
532}
533
534impl<'a, I> IteratorToValue<'a> for I
535where
536    I: Iterator<Item = ResultItem<'a, AnnotationData>>,
537{
538    fn value(&mut self) -> Option<&'a DataValue> {
539        self.next().map(|x| x.value())
540    }
541
542    fn value_as_str(&mut self) -> Option<&'a str> {
543        self.filter_map(|x| {
544            if let DataValue::String(s) = x.value() {
545                Some(s.as_str())
546            } else {
547                None
548            }
549        })
550        .next()
551    }
552}
553
554impl<I: Iterator> ResultIter<I> {
555    pub(crate) fn new(inner: I, sorted: bool) -> Self {
556        Self {
557            inner: Some(inner),
558            sorted,
559        }
560    }
561
562    /// This does no sorting, it just tells that the iterator passed is sorted
563    pub(crate) fn new_sorted(inner: I) -> Self {
564        Self {
565            inner: Some(inner),
566            sorted: true,
567        }
568    }
569
570    /// This tells that the iterator passed is not sorted
571    pub(crate) fn new_unsorted(inner: I) -> Self {
572        Self {
573            inner: Some(inner),
574            sorted: false,
575        }
576    }
577
578    /// Creates a dummy iterator
579    pub(crate) fn new_empty() -> Self {
580        Self {
581            inner: None,
582            sorted: true,
583        }
584    }
585}
586
587impl<I: Iterator> Iterator for ResultIter<I> {
588    type Item = I::Item;
589
590    #[inline]
591    fn next(&mut self) -> Option<Self::Item> {
592        if let Some(inner) = self.inner.as_mut() {
593            inner.next()
594        } else {
595            None
596        }
597    }
598}
599
600pub struct FilterAllIter<'store, T, I>
601where
602    T: Storable + 'store,
603    I: Iterator<Item = ResultItem<'store, T>>,
604{
605    inner: I,
606    filter: Handles<'store, T>,
607    store: &'store AnnotationStore,
608    buffer: Option<Handles<'store, T>>,
609    cursor: usize,
610}
611
612impl<'store, T, I> FilterAllIter<'store, T, I>
613where
614    T: Storable + 'store,
615    I: Iterator<Item = ResultItem<'store, T>>,
616    I: ToHandles<'store, T>,
617{
618    pub fn new(inner: I, filter: Handles<'store, T>, store: &'store AnnotationStore) -> Self {
619        Self {
620            inner,
621            filter,
622            store,
623            buffer: None,
624            cursor: 0,
625        }
626    }
627}
628
629impl<'store, T, I> Iterator for FilterAllIter<'store, T, I>
630where
631    T: Storable + 'store,
632    I: Iterator<Item = ResultItem<'store, T>>,
633    Self: FullHandleToResultItem<'store, T>,
634    I: ToHandles<'store, T>,
635{
636    type Item = I::Item;
637    fn next(&mut self) -> Option<Self::Item> {
638        if self.buffer.is_none() {
639            let buffer = self.inner.to_handles(self.store);
640            if !self.filter.iter().all(|h| buffer.contains(&h)) {
641                //constraint not satisfied, iterator yields nothing:
642                //all filter items must be in the buffer
643                return None;
644            }
645            self.buffer = Some(buffer);
646        }
647        //drain the buffer
648        let buffer = self
649            .buffer
650            .as_mut()
651            .expect("buffer must exist at this point");
652
653        if let Some(handle) = buffer.get(self.cursor) {
654            self.cursor += 1;
655            self.get_item(handle)
656        } else {
657            None
658        }
659    }
660}
661
662/// An iterator that can extract an arbitrary subrange, even
663/// with relative coordinates (at which point it will allocate a buffer)
664pub trait LimitIterator<I>
665where
666    I: Iterator,
667{
668    fn limit(self, begin: isize, end: isize) -> LimitIter<I>;
669}
670
671impl<I> LimitIterator<I> for I
672where
673    I: Iterator,
674{
675    fn limit(self, begin: isize, end: isize) -> LimitIter<I> {
676        LimitIter {
677            begin,
678            end,
679            cursor: 0,
680            buffer: VecDeque::new(),
681            emptybuffer: false,
682            inner: self,
683        }
684    }
685}
686
687pub struct LimitIter<I>
688where
689    I: Iterator,
690{
691    inner: I,
692    cursor: isize,
693    begin: isize,
694    /// end=0 means until the end, negative numbers are relative to the end. End is non-inclusive.
695    end: isize,
696    emptybuffer: bool,
697    buffer: VecDeque<I::Item>,
698}
699
700impl<I> Iterator for LimitIter<I>
701where
702    I: Iterator,
703{
704    type Item = I::Item;
705
706    fn next(&mut self) -> Option<Self::Item> {
707        loop {
708            if self.emptybuffer {
709                return self.buffer.pop_front();
710            } else if let Some(item) = self.inner.next() {
711                if self.begin >= 0 && self.cursor >= self.begin {
712                    if self.end == 0 || self.cursor < self.end {
713                        //this is the simple case
714                        self.cursor += 1;
715                        return Some(item);
716                    } else if self.end > 0 && self.cursor >= self.end {
717                        self.cursor += 1;
718                        return None;
719                    }
720                    //else fall back to buffer..
721                }
722
723                //else, if all absolute/positive constraints (if any) are respected, add to buffer
724                if ((self.begin < 0) || (self.begin >= 0 && self.cursor >= self.begin))
725                    && ((self.end <= 0) || (self.cursor < self.end))
726                {
727                    self.buffer.push_back(item);
728                    if self.end == 0 && self.begin < 0 {
729                        // we only need to keep part of the buffer in this case
730                        if self.buffer.len() > self.begin.abs() as usize {
731                            let excess = self.buffer.len() - self.begin.abs() as usize;
732                            for _ in 0..excess {
733                                self.buffer.pop_front();
734                            }
735                        }
736                    }
737                }
738                self.cursor += 1;
739            } else {
740                //we reached the end, no item left in inner iterator
741                if self.begin >= 0 && self.end >= 0 {
742                    //all done
743                    return None;
744                } else {
745                    //now we can empty the buffer (on next iteration of the main loop)
746                    self.emptybuffer = true;
747                    // but first we prune unneeded items:
748                    if self.end < 0 && self.begin < 0 {
749                        //discard items from the begin which we do not want
750                        for _ in 0..self.begin.abs() {
751                            self.buffer.pop_front();
752                        }
753                    }
754                    if self.end < 0 {
755                        //discard some items at the end which we do not want
756                        for _ in 0..self.end.abs() {
757                            self.buffer.pop_back();
758                        }
759                    }
760                }
761            }
762        }
763    }
764}
765
766// Auxiliary data structures the API relies on internally:
767
768#[derive(Clone, Debug, Copy, PartialEq, Eq)]
769/// This determines how a filter is applied when there the filter is provided with multiple
770/// reference instances to match against. It determines if the filter requires a match with
771/// any of the instances (default), or with all of them.
772pub enum FilterMode {
773    /// The filter succeeds if any match is found.
774    Any,
775
776    /// The filter only succeeds if all reference instances are matched.
777    /// Note: This might not make sense in some contexts!
778    All,
779}
780
781impl Default for FilterMode {
782    fn default() -> Self {
783        Self::Any
784    }
785}
786
787#[derive(Clone, Debug, Copy, PartialEq, Eq)]
788/// Determines whether a text search is exact (case sensitive) or case insensitive.
789pub enum TextMode {
790    Exact,
791    CaseInsensitive,
792}
793
794impl Default for TextMode {
795    fn default() -> Self {
796        Self::Exact
797    }
798}
799
800#[derive(Debug)]
801/// This is a low-level data structure that holds filter states for the iterators [`FilteredAnnotations`], [`FilteredData`], [`FilteredResources`],[`FilteredTextSelections`].
802/// You likely do not need this and should use the appropriate `filter_*` methods on the iterators instead.
803/// The only possible use from outside is in programmatically setting direct query constraints via [`Constraint::Filter`].
804#[allow(dead_code)] //some of the Filters have fields that are not used
805pub(crate) enum Filter<'store> {
806    AnnotationData(
807        AnnotationDataSetHandle,
808        AnnotationDataHandle,
809        SelectionQualifier,
810    ),
811    AnnotationDataSet(AnnotationDataSetHandle, SelectionQualifier),
812    DataKey(AnnotationDataSetHandle, DataKeyHandle, SelectionQualifier),
813    DataKeyAndOperator(
814        AnnotationDataSetHandle,
815        DataKeyHandle,
816        DataOperator<'store>,
817        SelectionQualifier,
818    ),
819
820    Annotation(AnnotationHandle, SelectionQualifier, AnnotationDepth),
821
822    TextResource(TextResourceHandle, SelectionQualifier),
823
824    DataOperator(DataOperator<'store>, SelectionQualifier),
825    TextSelectionOperator(TextSelectionOperator, SelectionQualifier),
826    AnnotationSubStore(Option<AnnotationSubStoreHandle>),
827
828    Annotations(
829        Handles<'store, Annotation>,
830        FilterMode,
831        SelectionQualifier,
832        AnnotationDepth,
833    ),
834    Resources(
835        Handles<'store, TextResource>,
836        FilterMode,
837        SelectionQualifier,
838    ),
839    Data(
840        Handles<'store, AnnotationData>,
841        FilterMode,
842        SelectionQualifier,
843    ),
844    Keys(Handles<'store, DataKey>, FilterMode, SelectionQualifier),
845    DataSets(
846        Handles<'store, AnnotationDataSet>,
847        FilterMode,
848        SelectionQualifier,
849    ),
850    Text(String, TextMode, &'store str), //the last string represents the delimiter for joining text
851    Regex(Regex, &'store str),           //the last string represents the delimiter for joining text
852    TextSelection(TextResourceHandle, TextSelectionHandle),
853    TextSelections(Handles<'store, TextSelection>, FilterMode),
854    Offset(Offset),
855
856    //these have the advantage the collections are external references
857    BorrowedAnnotations(
858        &'store Annotations<'store>,
859        FilterMode,
860        SelectionQualifier,
861        AnnotationDepth,
862    ),
863    BorrowedData(
864        &'store Handles<'store, AnnotationData>,
865        FilterMode,
866        SelectionQualifier,
867    ),
868    BorrowedKeys(
869        &'store Handles<'store, DataKey>,
870        FilterMode,
871        SelectionQualifier,
872    ),
873    BorrowedDataSets(
874        &'store Handles<'store, AnnotationDataSet>,
875        FilterMode,
876        SelectionQualifier,
877    ),
878    BorrowedText(&'store str, TextMode, &'store str), //the last string represents the delimiter for joining text
879    BorrowedResources(
880        &'store Handles<'store, TextResource>,
881        FilterMode,
882        SelectionQualifier,
883    ),
884}