Skip to main content

io_email/search/
parser.rs

1//! # Search emails query string parser
2//!
3//! Entry point for parsing a full search emails query from a string
4//! slice. Defers to [`super::filter::parser`] and
5//! [`super::sort::parser`] for the actual grammar.
6//!
7//! Parsing is based on [`chumsky`].
8
9use alloc::{string::String, vec::Vec};
10
11use chumsky::{Parser, error::Rich, extra};
12
13use crate::search::{
14    error::Error,
15    filter::{self, query::SearchEmailsFilterQuery},
16    query::SearchEmailsQuery,
17    sort::{self, query::SearchEmailsSorter},
18};
19
20/// Alias for a rich [`chumsky`] error for better diagnostics.
21pub type ParserError<'a> = extra::Err<Rich<'a, char>>;
22
23/// Parses `input` into a [`SearchEmailsQuery`].
24///
25/// Because of the recursive nature of [`SearchEmailsFilterQuery`], the
26/// string is splitted around the `"order by"` keyword and the two
27/// halves are parsed separately. A query string can be filter-only,
28/// sort-only, or filter then sort.
29///
30/// See [`filter::parser::query`] for the filter grammar and
31/// [`sort::parser::query`] for the sort grammar.
32pub fn parse(input: impl AsRef<str>) -> Result<SearchEmailsQuery, Error> {
33    let input = input.as_ref().trim();
34
35    if let Some((filters_input, sorters_input)) = input.rsplit_once("order by") {
36        if filters_input.trim().is_empty() {
37            let filter = None;
38            let sort = parse_sort(sorters_input).map(Some)?;
39            Ok(SearchEmailsQuery { filter, sort })
40        } else {
41            let filter = parse_filter(filters_input).map(Some)?;
42            let sort = parse_sort(sorters_input).map(Some)?;
43            Ok(SearchEmailsQuery { filter, sort })
44        }
45    } else {
46        let filter = parse_filter(input).map(Some)?;
47        let sort = None;
48        Ok(SearchEmailsQuery { filter, sort })
49    }
50}
51
52/// Parses `input` into a [`SearchEmailsFilterQuery`].
53pub fn parse_filter(input: impl AsRef<str>) -> Result<SearchEmailsFilterQuery, Error> {
54    let input = input.as_ref().trim();
55
56    filter::parser::query()
57        .parse(input)
58        .into_result()
59        .map_err(|errs| {
60            let errs = errs
61                .into_iter()
62                .map(|err| err.clone().into_owned())
63                .collect();
64            Error::ParseError(errs, String::from(input))
65        })
66}
67
68/// Parses `input` into a list of [`SearchEmailsSorter`].
69pub fn parse_sort(input: impl AsRef<str>) -> Result<Vec<SearchEmailsSorter>, Error> {
70    let input = input.as_ref().trim();
71
72    sort::parser::query()
73        .parse(input)
74        .into_result()
75        .map_err(|errs| {
76            let errs = errs
77                .into_iter()
78                .map(|err| err.clone().into_owned())
79                .collect();
80            Error::ParseError(errs, String::from(input))
81        })
82}