use crate::annotation::Annotation;
use crate::annotationdata::AnnotationData;
use crate::annotationdataset::{AnnotationDataSet, AnnotationDataSetHandle};
use crate::annotationstore::AnnotationStore;
use crate::api::*;
use crate::datakey::DataKey;
use crate::datavalue::DataOperator;
use crate::error::*;
use crate::resources::{PositionMode, TextResource, TextResourceHandle};
use crate::selector::{Offset, OffsetMode};
use crate::store::*;
use crate::text::Text;
use crate::textselection::{
ResultTextSelection, ResultTextSelectionSet, TestTextSelection, TextSelection,
TextSelectionHandle, TextSelectionOperator, TextSelectionSet,
};
use crate::types::*;
use crate::{Filter, FilterMode, TextMode};
use sealed::sealed;
use serde::ser::{SerializeStruct, Serializer};
use serde::Serialize;
use rayon::prelude::*;
use std::cmp::Ordering;
impl<'store> FullHandle<TextSelection> for ResultItem<'store, TextSelection> {
fn fullhandle(&self) -> <TextSelection as Storable>::FullHandleType {
(self.resource().handle(), self.handle())
}
}
impl<'store> ResultItem<'store, TextSelection> {
pub fn as_resulttextselection(self) -> ResultTextSelection<'store> {
self.into()
}
pub fn begin(&self) -> usize {
self.as_ref().begin()
}
pub fn end(&self) -> usize {
self.as_ref().end()
}
pub fn resource(&self) -> ResultItem<'store, TextResource> {
let rootstore = self.rootstore();
self.store().as_resultitem(rootstore, rootstore)
}
pub fn annotations(&self) -> ResultIter<impl Iterator<Item = ResultItem<'store, Annotation>>> {
if let Some(annotations) = self
.rootstore()
.annotations_by_textselection(self.store().handle().unwrap(), self.as_ref())
{
ResultIter::new(
FromHandles::new(annotations.iter().copied(), self.rootstore()),
true,
)
} else {
ResultIter::new_empty()
}
}
pub fn annotations_len(&self) -> usize {
if let Some(vec) = self
.rootstore()
.annotations_by_textselection(self.store().handle().unwrap(), self.as_ref())
{
vec.len()
} else {
0
}
}
pub fn related_text(
&self,
operator: TextSelectionOperator,
) -> impl Iterator<Item = ResultTextSelection<'store>> {
let tset: TextSelectionSet = self.clone().into();
self.resource().related_text(operator, tset)
}
}
impl<'store> Serialize for ResultTextSelection<'store> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("TextSelection", 4)?;
state.serialize_field("@type", "TextSelection")?;
state.serialize_field("begin", &self.begin())?;
state.serialize_field("end", &self.end())?;
state.serialize_field("text", &self.text())?;
state.end()
}
}
impl<'store> ResultTextSelection<'store> {
pub fn inner(&self) -> &TextSelection {
match self {
Self::Bound(item) => item.as_ref(),
Self::Unbound(_, _, item) => item,
}
}
pub fn as_ref(&self) -> Option<&'store TextSelection> {
match self {
Self::Bound(item) => Some(item.as_ref()),
Self::Unbound(..) => None,
}
}
pub fn as_resultitem(&self) -> Option<&ResultItem<'store, TextSelection>> {
match self {
Self::Bound(item) => Some(item),
Self::Unbound(..) => None,
}
}
pub fn begin(&self) -> usize {
match self {
Self::Bound(item) => item.as_ref().begin(),
Self::Unbound(_, _, item) => item.begin(),
}
}
pub fn end(&self) -> usize {
match self {
Self::Bound(item) => item.as_ref().end(),
Self::Unbound(_, _, item) => item.end(),
}
}
pub fn relative_begin(&self, container: &ResultTextSelection<'store>) -> Option<usize> {
if self.store() != container.store() {
None
} else {
let container = match container {
Self::Bound(item) => item.as_ref(),
Self::Unbound(_, _, item) => &item,
};
match self {
Self::Bound(item) => item.as_ref().relative_begin(container),
Self::Unbound(_, _, item) => item.relative_begin(container),
}
}
}
pub fn relative_end(&self, container: &ResultTextSelection<'store>) -> Option<usize> {
let container = match container {
Self::Bound(item) => item.as_ref(),
Self::Unbound(_, _, item) => &item,
};
match self {
Self::Bound(item) => item.as_ref().relative_end(container),
Self::Unbound(_, _, item) => item.relative_end(container),
}
}
pub fn relative_offset(
&self,
container: &ResultTextSelection<'store>,
offsetmode: OffsetMode,
) -> Option<Offset> {
let container = match container {
Self::Bound(item) => item.as_ref(),
Self::Unbound(_, _, item) => &item,
};
match self {
Self::Bound(item) => item.as_ref().relative_offset(container, offsetmode),
Self::Unbound(_, _, item) => item.relative_offset(container, offsetmode),
}
}
pub fn absolute_offset(&self, offset: &Offset) -> Result<Offset, StamError> {
match self {
Self::Bound(item) => item.as_ref().absolute_offset(offset),
Self::Unbound(_, _, item) => item.absolute_offset(offset),
}
}
pub(crate) fn store(&self) -> &'store TextResource {
match self {
Self::Bound(item) => item.store(),
Self::Unbound(_, store, ..) => store,
}
}
pub fn rootstore(&self) -> &'store AnnotationStore {
match self {
Self::Bound(item) => item.rootstore(),
Self::Unbound(rootstore, ..) => rootstore,
}
}
pub fn resource(&self) -> ResultItem<'store, TextResource> {
let rootstore = self.rootstore();
self.store().as_resultitem(rootstore, rootstore)
}
pub fn handle(&self) -> Option<TextSelectionHandle> {
match self {
Self::Bound(item) => Some(item.handle()),
Self::Unbound(..) => None,
}
}
pub fn take(self) -> Result<TextSelection, StamError> {
match self {
Self::Bound(_) => Err(StamError::AlreadyBound(
"Item is bound, can't be taken out!",
)),
Self::Unbound(_, _, item) => Ok(item),
}
}
pub fn annotations(&self) -> ResultIter<impl Iterator<Item = ResultItem<'store, Annotation>>> {
match self {
Self::Bound(item) => item.annotations(),
Self::Unbound(..) => ResultIter::new_empty(),
}
}
pub fn annotations_len(&self) -> usize {
match self {
Self::Bound(item) => item.annotations_len(),
Self::Unbound(..) => 0,
}
}
pub fn related_text(
&self,
operator: TextSelectionOperator,
) -> impl Iterator<Item = ResultTextSelection<'store>> {
let mut tset: TextSelectionSet =
TextSelectionSet::new(self.store().handle().expect("resource must have handle"));
tset.add(match self {
Self::Bound(item) => item.as_ref().clone().into(),
Self::Unbound(_, _, textselection) => textselection.clone(),
});
self.resource().related_text(operator, tset)
}
pub fn positions<'a>(&'a self, mode: PositionMode) -> Box<dyn Iterator<Item = &'a usize> + 'a> {
self.resource()
.as_ref()
.positions_in_range(mode, self.begin(), self.end())
}
pub fn segmentation<'a>(&'a self) -> SegmentationIter<'a> {
self.resource()
.segmentation_in_range(self.begin(), self.end())
}
pub fn to_json(&self) -> Result<String, StamError> {
serde_json::to_string_pretty(&self).map_err(|e| {
StamError::SerializationError(format!("Serializing textselection to string: {}", e))
})
}
pub fn test(&self, operator: &TextSelectionOperator, other: &ResultTextSelection) -> bool {
if self.resource() != other.resource() {
false
} else {
self.inner()
.test(operator, other.inner(), self.resource().as_ref())
}
}
pub fn test_set(
&self,
operator: &TextSelectionOperator,
other: &ResultTextSelectionSet,
) -> bool {
if self.resource() != other.resource() {
false
} else {
self.inner()
.test_set(operator, other.inner(), self.resource().as_ref())
}
}
pub fn to_json_string(&self) -> Result<String, StamError> {
let json = TextSelectionJson {
resource: self.resource().id().expect("resource must have ID"),
begin: self.begin(),
end: self.end(),
text: self.text(),
};
serde_json::to_string_pretty(&json).map_err(|e| {
StamError::SerializationError(format!("Writing textannotation to string: {}", e))
})
}
pub fn to_json_value(&self) -> Result<serde_json::Value, StamError> {
let json = TextSelectionJson {
resource: self.resource().id().expect("resource must have ID"),
begin: self.begin(),
end: self.end(),
text: self.text(),
};
serde_json::to_value(&json).map_err(|e| {
StamError::SerializationError(format!("Writing textannotation to JSON value: {}", e))
})
}
}
#[derive(Serialize)]
struct TextSelectionJson<'a> {
resource: &'a str,
begin: usize,
end: usize,
text: &'a str,
}
impl<'store> ResultTextSelectionSet<'store> {
pub fn rootstore(&self) -> &'store AnnotationStore {
self.rootstore
}
pub fn resource(&self) -> ResultItem<'store, TextResource> {
self.rootstore()
.resource(self.tset.resource())
.expect("resource must exist")
}
pub fn related_text(
self,
operator: TextSelectionOperator,
) -> impl Iterator<Item = ResultTextSelection<'store>> {
let resource = self.resource().as_ref();
let rootstore = self.rootstore();
ResultTextSelections::new(
resource
.textselections_by_operator(operator, self.tset)
.filter_map(|handle| {
resource
.get(handle)
.ok()
.map(|x| x.as_resultitem(resource, rootstore))
}),
)
}
pub fn test(&self, operator: &TextSelectionOperator, other: &ResultTextSelection) -> bool {
if self.resource() != other.resource() {
false
} else {
self.inner()
.test(operator, other.inner(), self.resource().as_ref())
}
}
pub fn test_set(
&self,
operator: &TextSelectionOperator,
other: &ResultTextSelectionSet,
) -> bool {
if self.resource() != other.resource() {
false
} else {
self.inner()
.test_set(operator, other.inner(), self.resource().as_ref())
}
}
}
pub trait SortTextualOrder<T>
where
T: PartialOrd,
{
fn textual_order(&mut self) -> Vec<T>;
}
pub fn compare_annotation_textual_order<'store>(
a: &ResultItem<'store, Annotation>,
b: &ResultItem<'store, Annotation>,
) -> Ordering {
let tset_a: TextSelectionSet = a.textselections().collect();
let tset_b: TextSelectionSet = b.textselections().collect();
if tset_a.is_empty() && tset_b.is_empty() {
a.handle().cmp(&b.handle())
} else if tset_a.is_empty() {
Ordering::Greater
} else if tset_b.is_empty() {
Ordering::Less
} else {
tset_a
.partial_cmp(&tset_b)
.expect("textual_order() can only be applied if annotations reference text!")
}
}
impl<'store, I> SortTextualOrder<ResultItem<'store, Annotation>> for I
where
I: Iterator<Item = ResultItem<'store, Annotation>>,
{
fn textual_order(&mut self) -> Vec<ResultItem<'store, Annotation>> {
let mut v: Vec<_> = self.collect();
v.sort_unstable_by(compare_annotation_textual_order);
v
}
}
impl<'store, I> SortTextualOrder<ResultTextSelection<'store>> for I
where
I: Iterator<Item = ResultTextSelection<'store>>,
{
fn textual_order(&mut self) -> Vec<ResultTextSelection<'store>> {
let mut v: Vec<_> = self.collect();
v.sort_unstable_by(|a, b| {
a.partial_cmp(b)
.expect("PartialOrd must work for ResultTextSelection")
});
v.dedup();
v
}
}
impl<'store, I> SortTextualOrder<ResultTextSelectionSet<'store>> for I
where
I: Iterator<Item = ResultTextSelectionSet<'store>>,
{
fn textual_order(&mut self) -> Vec<ResultTextSelectionSet<'store>> {
let mut v: Vec<_> = self.collect();
v.sort_unstable_by(|a, b| {
if a.tset.is_empty() && b.tset.is_empty() {
Ordering::Equal
} else if a.tset.is_empty() {
Ordering::Greater
} else if b.tset.is_empty() {
Ordering::Less
} else {
a.partial_cmp(b)
.expect("PartialOrd must work for ResultTextSelectionSet")
}
});
v
}
}
impl<'store, I> SortTextualOrder<TextSelectionSet> for I
where
I: Iterator<Item = TextSelectionSet>,
{
fn textual_order(&mut self) -> Vec<TextSelectionSet> {
let mut v: Vec<_> = self.collect();
v.sort_unstable_by(|a, b| {
if a.is_empty() && b.is_empty() {
Ordering::Equal
} else if a.is_empty() {
Ordering::Greater
} else if b.is_empty() {
Ordering::Less
} else {
a.partial_cmp(b)
.expect("PartialOrd must work for TextSelectionSet")
}
});
v
}
}
impl<'store, I> SortTextualOrder<TextSelection> for I
where
I: Iterator<Item = TextSelection>,
{
fn textual_order(&mut self) -> Vec<TextSelection> {
let mut v: Vec<_> = self.collect();
v.sort_unstable_by(|a, b| a.cmp(b));
v.dedup();
v
}
}
impl<'store, I> FullHandleToResultItem<'store, TextSelection>
for FromHandles<'store, TextSelection, I>
where
I: Iterator<Item = (TextResourceHandle, TextSelectionHandle)>,
{
fn get_item(
&self,
handle: (TextResourceHandle, TextSelectionHandle),
) -> Option<ResultItem<'store, TextSelection>> {
if let Some(resource) = self.store.resource(handle.0) {
let ts: &TextSelection = resource.as_ref().get(handle.1).unwrap();
Some(ts.as_resultitem(resource.as_ref(), self.store))
} else {
None
}
}
}
#[sealed]
impl TypeInfo for Option<ResultTextSelection<'_>> {
fn typeinfo() -> Type {
Type::TextSelection
}
}
#[sealed]
impl TypeInfo for ResultTextSelection<'_> {
fn typeinfo() -> Type {
Type::TextSelection
}
}
pub type TextSelections<'store> = Handles<'store, TextSelection>;
pub struct ResultTextSelections<'store, I>
where
I: Iterator<Item = ResultItem<'store, TextSelection>>,
{
inner: I,
}
impl<'store, I> ResultTextSelections<'store, I>
where
I: Iterator<Item = ResultItem<'store, TextSelection>>,
{
pub fn new(inner: I) -> Self {
Self { inner }
}
}
impl<'store, I> Iterator for ResultTextSelections<'store, I>
where
I: Iterator<Item = ResultItem<'store, TextSelection>>,
{
type Item = ResultTextSelection<'store>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner
.next()
.map(|textselection| ResultTextSelection::Bound(textselection))
}
}
impl<'store, I> DoubleEndedIterator for ResultTextSelections<'store, I>
where
I: DoubleEndedIterator<Item = ResultItem<'store, TextSelection>>,
{
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.inner
.next_back()
.map(|textselection| ResultTextSelection::Bound(textselection))
}
}
pub trait TextSelectionIterator<'store>: Iterator<Item = ResultTextSelection<'store>>
where
Self: Sized,
{
fn parallel(self) -> rayon::vec::IntoIter<ResultTextSelection<'store>> {
let annotations: Vec<_> = self.collect();
annotations.into_par_iter()
}
fn annotations(
self,
) -> ResultIter<<Vec<ResultItem<'store, Annotation>> as IntoIterator>::IntoIter> {
let mut annotations: Vec<_> = self
.map(|resource| resource.annotations())
.flatten()
.collect();
annotations.sort_unstable();
annotations.dedup();
ResultIter::new_sorted(annotations.into_iter())
}
fn related_text(
self,
operator: TextSelectionOperator,
) -> <Vec<ResultTextSelection<'store>> as IntoIterator>::IntoIter {
let mut textselections: Vec<ResultTextSelection<'store>> = Vec::new();
for textselection in self {
textselections.extend(textselection.related_text(operator))
}
textselections.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
textselections.dedup();
textselections.into_iter()
}
fn text(self) -> TextIter<'store, Self> {
TextIter { inner: self }
}
fn text_join(self, delimiter: &str) -> String {
let mut s = String::new();
for textselection in self {
let text = textselection.text();
if !s.is_empty() {
s += delimiter;
}
s += text;
}
s
}
fn text_simple(self) -> Option<&'store str> {
let mut iter = self.text();
let text = iter.next();
if let None = iter.next() {
return text;
} else {
None
}
}
fn filter_annotations(
self,
annotations: Annotations<'store>,
mode: FilterMode,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::Annotations(
annotations,
mode,
SelectionQualifier::Normal,
AnnotationDepth::One,
),
}
}
fn filter_annotations_byref(
self,
annotations: &'store Annotations<'store>,
mode: FilterMode,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::BorrowedAnnotations(
annotations,
mode,
SelectionQualifier::Normal,
AnnotationDepth::One,
),
}
}
fn filter_annotation(
self,
annotation: &ResultItem<Annotation>,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::Annotation(
annotation.handle(),
SelectionQualifier::Normal,
AnnotationDepth::One,
),
}
}
fn filter_text(
self,
text: String,
case_sensitive: bool,
) -> FilteredTextSelections<'store, Self> {
if case_sensitive {
FilteredTextSelections {
inner: self,
filter: Filter::Text(text, TextMode::Exact, ""), }
} else {
FilteredTextSelections {
inner: self,
filter: Filter::Text(text.to_lowercase(), TextMode::CaseInsensitive, ""),
}
}
}
fn filter_text_byref(
self,
text: &'store str,
case_sensitive: bool,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::BorrowedText(
text,
if case_sensitive {
TextMode::Exact
} else {
TextMode::CaseInsensitive
},
"",
),
}
}
fn filter_text_regex(self, regex: Regex) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::Regex(regex, ""),
}
}
fn filter_data(
self,
data: Data<'store>,
mode: FilterMode,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::Data(data, mode, SelectionQualifier::Normal),
}
}
fn filter_data_byref(
self,
data: &'store Data<'store>,
mode: FilterMode,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::BorrowedData(data, mode, SelectionQualifier::Normal),
}
}
fn filter_data_all_byref(
self,
data: &'store Data<'store>,
mode: FilterMode,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::BorrowedData(data, mode, SelectionQualifier::Normal),
}
}
fn filter_annotationdata(
self,
data: &ResultItem<'store, AnnotationData>,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::AnnotationData(
data.set().handle(),
data.handle(),
SelectionQualifier::Normal,
),
}
}
fn filter_key_value(
self,
key: &ResultItem<'store, DataKey>,
value: DataOperator<'store>,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::DataKeyAndOperator(
key.set().handle(),
key.handle(),
value,
SelectionQualifier::Normal,
),
}
}
fn filter_key(self, key: &ResultItem<'store, DataKey>) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::DataKey(key.set().handle(), key.handle(), SelectionQualifier::Normal),
}
}
fn filter_key_handle(
self,
set: AnnotationDataSetHandle,
key: DataKeyHandle,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::DataKey(set, key, SelectionQualifier::Normal),
}
}
fn filter_value(self, value: DataOperator<'store>) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::DataOperator(value, SelectionQualifier::Normal),
}
}
fn filter_key_handle_value(
self,
set: AnnotationDataSetHandle,
key: DataKeyHandle,
value: DataOperator<'store>,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::DataKeyAndOperator(set, key, value, SelectionQualifier::Normal),
}
}
fn filter_set(
self,
set: &ResultItem<'store, AnnotationDataSet>,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::AnnotationDataSet(set.handle(), SelectionQualifier::Normal),
}
}
fn filter_set_handle(
self,
set: AnnotationDataSetHandle,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::AnnotationDataSet(set, SelectionQualifier::Normal),
}
}
fn filter_resource(
self,
resource: &ResultItem<'store, TextResource>,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::TextResource(resource.handle(), SelectionQualifier::Normal),
}
}
fn filter_one(
self,
textselection: &ResultItem<'store, TextSelection>, ) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::TextSelection(
textselection.resource().handle(),
textselection.handle(),
),
}
}
fn filter_any(
self,
textselections: Handles<'store, TextSelection>,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::TextSelections(textselections, FilterMode::Any),
}
}
fn filter_handle(
self,
resource: TextResourceHandle,
textselection: TextSelectionHandle,
) -> FilteredTextSelections<'store, Self> {
FilteredTextSelections {
inner: self,
filter: Filter::TextSelection(resource, textselection),
}
}
fn to_handles(&mut self, store: &'store AnnotationStore) -> Handles<'store, TextSelection> {
Handles::from_iter(
self.filter_map(|item| {
if let Some(handle) = item.handle() {
Some((item.resource().handle(), handle))
} else {
None
}
}),
store,
)
}
}
impl<'store, I> TextSelectionIterator<'store> for I
where
I: Iterator<Item = ResultTextSelection<'store>>,
{
}
pub struct FilteredTextSelections<'store, I>
where
I: Iterator<Item = ResultTextSelection<'store>>,
{
inner: I,
filter: Filter<'store>,
}
impl<'store, I> Iterator for FilteredTextSelections<'store, I>
where
I: Iterator<Item = ResultTextSelection<'store>>,
{
type Item = ResultTextSelection<'store>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(textselection) = self.inner.next() {
if self.test_filter(&textselection) {
return Some(textselection);
}
} else {
return None;
}
}
}
}
impl<'store, I> FilteredTextSelections<'store, I>
where
I: Iterator<Item = ResultTextSelection<'store>>,
{
fn test_filter(&self, textselection: &ResultTextSelection<'store>) -> bool {
match &self.filter {
Filter::TextSelection(res_handle, ts_handle) => {
textselection.resource().handle() == *res_handle
&& textselection.handle() == Some(*ts_handle)
}
Filter::TextSelections(handles, FilterMode::Any) => {
if let Some(textselection) = textselection.as_resultitem() {
handles.contains(&textselection.fullhandle())
} else {
false
}
}
Filter::TextSelections(_handles, FilterMode::All) => {
unreachable!("not implemented here")
}
Filter::Resources(handles, FilterMode::Any, _) => {
handles.contains(&textselection.resource().handle())
}
Filter::Annotations(annotations, mode, _, AnnotationDepth::One) => textselection
.annotations()
.filter_annotations_byref(annotations, *mode)
.test(),
Filter::BorrowedAnnotations(annotations, mode, _, AnnotationDepth::One) => {
textselection
.annotations()
.filter_annotations_byref(annotations, *mode)
.test()
}
Filter::Annotation(annotation, SelectionQualifier::Normal, AnnotationDepth::One) => {
textselection
.annotations()
.filter_handle(*annotation)
.test()
}
Filter::TextResource(res_handle, _) => textselection.resource().handle() == *res_handle,
Filter::Text(reftext, textmode, _) => {
let text = textselection.text();
if *textmode == TextMode::CaseInsensitive {
text.to_lowercase().as_str() == reftext
} else {
text == reftext.as_str()
}
}
Filter::BorrowedText(reftext, textmode, _) => {
let text = textselection.text();
if *textmode == TextMode::CaseInsensitive {
text.to_lowercase().as_str() == *reftext
} else {
text == *reftext
}
}
Filter::Regex(regex, _) => regex.is_match(textselection.text()),
Filter::TextSelectionOperator(operator, _) => {
textselection.related_text(*operator).test()
}
Filter::Data(data, mode, _) => textselection
.annotations()
.filter_data_byref(data, *mode)
.test(),
Filter::BorrowedData(data, mode, _) => textselection
.annotations()
.filter_data_byref(data, *mode)
.test(),
Filter::DataKey(set, key, _) => textselection
.annotations()
.data()
.filter_key_handle(*set, *key)
.test(),
Filter::DataKeyAndOperator(set, key, value, _) => textselection
.annotations()
.data()
.filter_key_handle_value(*set, *key, value.clone())
.test(),
Filter::DataOperator(value, _) => textselection
.annotations()
.data()
.filter_value(value.clone())
.test(),
Filter::AnnotationDataSet(set, _) => textselection
.annotations()
.data()
.filter_set_handle(*set)
.test(),
Filter::AnnotationData(set, data, _) => textselection
.annotations()
.data()
.filter_handle(*set, *data)
.test(),
_ => unreachable!(
"Filter {:?} not implemented for FilteredTextSelections",
self.filter
),
}
}
}
pub struct TextIter<'store, I>
where
I: Iterator<Item = ResultTextSelection<'store>>,
{
inner: I,
}
impl<'store, I> Iterator for TextIter<'store, I>
where
I: Iterator<Item = ResultTextSelection<'store>>,
{
type Item = &'store str;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|ts| ts.text())
}
}
impl<'store> From<ResultItem<'store, TextResource>> for ResultTextSelection<'store> {
fn from(resource: ResultItem<'store, TextResource>) -> Self {
resource.to_textselection()
}
}
impl<'store> TryFrom<&ResultItem<'store, Annotation>> for ResultTextSelectionSet<'store> {
type Error = StamError;
fn try_from(annotation: &ResultItem<'store, Annotation>) -> Result<Self, Self::Error> {
let mut invalid = false;
let invalid = &mut invalid;
let mut foundresource: Option<TextResourceHandle> = None;
let tset: TextSelectionSet = annotation
.textselections()
.take_while(|tsel| {
if foundresource.is_some() {
if foundresource.unwrap() == tsel.resource().handle() {
true
} else {
*invalid = true;
false
}
} else {
foundresource = Some(tsel.resource().handle());
true
}
})
.collect();
if *invalid || tset.is_empty() {
Err(StamError::NoText(
"conversion Annotation->TextSelectionSet failed: Annotation does not reference any text or text does not pertain to a single resource",
))
} else {
Ok(ResultTextSelectionSet {
tset,
rootstore: annotation.store(),
})
}
}
}
impl<'store> FromIterator<ResultTextSelection<'store>> for ResultTextSelectionSet<'store> {
fn from_iter<T: IntoIterator<Item = ResultTextSelection<'store>>>(iter: T) -> Self {
let mut store: Option<&'store AnnotationStore> = None;
let tset: TextSelectionSet = iter
.into_iter()
.inspect(|x| store = Some(x.rootstore()))
.collect();
ResultTextSelectionSet {
tset,
rootstore: store.expect("Iterator may not be empty"), }
}
}