#[derive(Debug, Clone, PartialEq)]
pub enum TextQuery {
Match(String),
MatchAll(String),
Phrase {
text: String,
slop: u32,
},
Prefix(String),
And(Vec<TextQuery>),
Or(Vec<TextQuery>),
Not(Box<TextQuery>),
}
impl TextQuery {
pub fn all<I: IntoIterator<Item = TextQuery>>(queries: I) -> Self {
TextQuery::And(queries.into_iter().collect())
}
pub fn any<I: IntoIterator<Item = TextQuery>>(queries: I) -> Self {
TextQuery::Or(queries.into_iter().collect())
}
#[allow(clippy::should_implement_trait)]
pub fn not(inner: TextQuery) -> Self {
TextQuery::Not(Box::new(inner))
}
pub(super) fn is_empty(&self) -> bool {
match self {
TextQuery::Match(s) | TextQuery::MatchAll(s) | TextQuery::Prefix(s) => s.is_empty(),
TextQuery::Phrase { text, .. } => text.is_empty(),
TextQuery::And(q) | TextQuery::Or(q) => q.is_empty(),
TextQuery::Not(_) => false,
}
}
pub(super) fn is_purely_negative(&self) -> bool {
match self {
TextQuery::Not(_) => true,
TextQuery::And(qs) | TextQuery::Or(qs) => {
!qs.is_empty() && qs.iter().all(|q| q.is_purely_negative())
}
_ => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn convenience_constructors_compose() {
let q = TextQuery::all([
TextQuery::Match("a".into()),
TextQuery::any([TextQuery::Match("b".into()), TextQuery::Match("c".into())]),
TextQuery::not(TextQuery::Match("d".into())),
]);
match q {
TextQuery::And(parts) => assert_eq!(parts.len(), 3),
_ => panic!("expected And"),
}
}
#[test]
fn empty_match_is_empty() {
assert!(TextQuery::Match(String::new()).is_empty());
assert!(TextQuery::MatchAll(String::new()).is_empty());
assert!(TextQuery::Prefix(String::new()).is_empty());
assert!(TextQuery::Phrase {
text: String::new(),
slop: 0
}
.is_empty());
assert!(TextQuery::And(vec![]).is_empty());
assert!(TextQuery::Or(vec![]).is_empty());
}
#[test]
fn populated_match_is_not_empty() {
assert!(!TextQuery::Match("foo".into()).is_empty());
}
#[test]
fn purely_negative_detection() {
assert!(TextQuery::not(TextQuery::Match("a".into())).is_purely_negative());
assert!(
TextQuery::all([TextQuery::not(TextQuery::Match("a".into()))]).is_purely_negative()
);
assert!(!TextQuery::all([
TextQuery::Match("a".into()),
TextQuery::not(TextQuery::Match("b".into())),
])
.is_purely_negative());
assert!(!TextQuery::Match("a".into()).is_purely_negative());
assert!(!TextQuery::And(vec![]).is_purely_negative());
}
}