#[cfg(test)]
use pretty_assertions::assert_eq;
use sikula::{mir, prelude::*};
#[derive(Clone, Debug, PartialEq, Eq)]
enum ManualResource<'a> {
Author(&'a str),
Subject(Primary<'a>),
Message(Primary<'a>),
Read,
Size(Ordered<usize>),
Sent(Ordered<time::OffsetDateTime>),
Label(Qualified<'a, &'a str>),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum ManualResourceSortable {
Author,
Size,
Sent,
}
impl FromQualifier for ManualResourceSortable {
type Err = ();
fn from_qualifier(qualifier: &Qualifier) -> Result<Self, Self::Err> {
Ok(match qualifier.as_slice() {
["author"] => Self::Author,
["size"] => Self::Size,
["sent"] => Self::Sent,
_ => return Err(()),
})
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum ManualResourceScope {
Subject,
Message,
}
impl FromQualifier for ManualResourceScope {
type Err = ();
fn from_qualifier(qualifier: &Qualifier) -> Result<Self, Self::Err> {
Ok(match qualifier.as_slice() {
["subject"] => Self::Subject,
["message"] => Self::Message,
_ => return Err(()),
})
}
}
impl<'r> Search for ManualResource<'r> {
type Parsed<'a> = ManualResource<'a>;
type Sortable = ManualResourceSortable;
type Scope = ManualResourceScope;
fn default_scopes() -> Vec<Self::Scope> {
vec![Self::Scope::Subject]
}
fn translate_match<'a>(
context: &Context<'_, Self::Parsed<'a>>,
qualifier: Qualifier<'a>,
expression: mir::Expression<'a>,
) -> Result<Term<'a, Self::Parsed<'a>>, Error<'a>> {
let term = match expression {
mir::Expression::Predicate => match qualifier.as_slice() {
["read"] => Term::Match(Self::Parsed::<'a>::Read),
_ => return Err(Error::UnknownPredicate(qualifier)),
},
mir::Expression::Simple(expression) => match qualifier.as_slice() {
[] => {
let mut terms = vec![];
for scope in &context.scopes {
let expression = match scope {
ManualResourceScope::Subject => Term::Match(
Self::Parsed::<'a>::Subject(expression.into_expression(
QualifierContext::Primary,
Qualifier::empty(),
)?),
),
ManualResourceScope::Message => Term::Match(
Self::Parsed::<'a>::Message(expression.into_expression(
QualifierContext::Primary,
Qualifier::empty(),
)?),
),
};
terms.push(expression);
}
Term::Or(terms)
}
["message", n @ ..] => Term::Match(Self::Parsed::<'a>::Message(
expression.into_expression(QualifierContext::Qualifier, n.into())?,
)),
["subject", n @ ..] => Term::Match(Self::Parsed::<'a>::Subject(
expression.into_expression(QualifierContext::Qualifier, n.into())?,
)),
["author", n @ ..] => Term::Match(Self::Parsed::<'a>::Author(
expression.into_expression(QualifierContext::Qualifier, n.into())?,
)),
["sent", n @ ..] => Term::Match(Self::Parsed::<'a>::Sent(
expression.into_expression(QualifierContext::Qualifier, n.into())?,
)),
["size", n @ ..] => Term::Match(Self::Parsed::<'a>::Size(
expression.into_expression(QualifierContext::Qualifier, n.into())?,
)),
["label", n @ ..] => Term::Match(Self::Parsed::<'a>::Label(
expression.into_expression(QualifierContext::Qualifier, n.into())?,
)),
_ => return Err(Error::UnknownQualifier(qualifier)),
},
};
Ok(term)
}
}
#[test]
fn test() {
let r = ManualResource::parse(r#"is:read message:bar -subject:foo"#).unwrap();
assert_eq!(
Query {
term: Term::And(vec![
Term::Match(ManualResource::Read),
Term::Match(ManualResource::Message(Primary::Equal("bar"))),
Term::Not(Box::new(Term::Match(ManualResource::Subject(
Primary::Equal("foo")
))))
]),
sorting: vec![],
},
r
)
}
#[test]
fn test_scopes() {
let r = ManualResource::parse(r#"is:read bar foo in:subject in:message"#).unwrap();
assert_eq!(
Query {
term: Term::And(vec![
Term::Match(ManualResource::Read),
Term::Or(vec![
Term::Match(ManualResource::Subject(Primary::Partial("bar"))),
Term::Match(ManualResource::Message(Primary::Partial("bar"))),
]),
Term::Or(vec![
Term::Match(ManualResource::Subject(Primary::Partial("foo"))),
Term::Match(ManualResource::Message(Primary::Partial("foo"))),
])
]),
sorting: vec![],
},
r
)
}
#[test]
fn test_scopes_grouped() {
let r = ManualResource::parse(r#"bar (foo in:message)"#).unwrap();
assert_eq!(
Query {
term: Term::And(vec![
Term::Or(vec![Term::Match(ManualResource::Subject(
Primary::Partial("bar")
)),]),
Term::And(vec![Term::Or(vec![Term::Match(ManualResource::Message(
Primary::Partial("foo")
)),])])
]),
sorting: vec![],
},
r
)
}
fn assert_query<'a>(q: &'a str, expected: Query<'a, ManualResource<'a>>) {
let r = ManualResource::parse(q).unwrap();
assert_eq!(expected, r)
}
fn assert_term<'a>(q: &'a str, term: Term<'a, ManualResource<'a>>) {
assert_query(
q,
Query {
term,
sorting: Default::default(),
},
)
}
#[test]
fn test_range() {
assert_term(
r"size:1..2",
Term::Match(ManualResource::Size(Ordered::from_range(1..2))),
);
}
#[test]
fn test_range_1() {
assert_term(
r"size:*..2",
Term::Match(ManualResource::Size(Ordered::from_range(..2))),
);
}
#[test]
fn test_range_2() {
assert_term(
r"size:1..*",
Term::Match(ManualResource::Size(Ordered::from_range(1..))),
);
}
#[test]
fn test_range_3() {
assert_term(
r"size:*..*",
Term::Match(ManualResource::Size(Ordered::from_range(..))),
);
}
#[test]
fn test_equal() {
assert_term(
r#"message:a"#,
Term::Match(ManualResource::Message(Primary::Equal("a"))),
);
}
#[test]
fn test_partial() {
assert_term(
r#"a in:message"#,
Term::And(vec![Term::Or(vec![Term::Match(ManualResource::Message(
Primary::Partial("a"),
))])]),
);
}
#[test]
fn test_or_1() {
assert_term(
r#"((message:"a") OR (message:"b"))"#,
Term::Or(vec![
Term::Match(ManualResource::Message(Primary::Equal("a"))),
Term::Match(ManualResource::Message(Primary::Equal("b"))),
]),
);
}
#[test]
fn test_or_2() {
assert_term(
r#"((a in:message) OR (b in:message))"#,
Term::Or(vec![
Term::And(vec![Term::Or(vec![Term::Match(ManualResource::Message(
Primary::Partial("a"),
))])]),
Term::And(vec![Term::Or(vec![Term::Match(ManualResource::Message(
Primary::Partial("b"),
))])]),
]),
);
}
#[test]
fn test_compaction() {
let term: Term<'_, ManualResource<'_>> = Term::Or(vec![
Term::And(vec![Term::Or(vec![Term::Match(ManualResource::Message(
Primary::Partial("a"),
))])]),
Term::And(vec![Term::Or(vec![Term::Match(ManualResource::Message(
Primary::Partial("b"),
))])]),
]);
let compacted = term.compact();
assert_eq!(
compacted,
Term::Or(vec![
Term::Match(ManualResource::Message(Primary::Partial("a"))),
Term::Match(ManualResource::Message(Primary::Partial("b")))
])
);
}
#[test]
fn test_empty_query() {
let mut r = ManualResource::parse("message:a").unwrap();
assert!(!r.term.is_empty());
r.term = r.term.compact();
assert!(!r.term.is_empty());
let mut r = ManualResource::parse("sort:size").unwrap();
assert!(r.term.is_empty());
r.term = r.term.compact();
assert!(r.term.is_empty());
}