email/email/search_query/
mod.rs

1//! # Search emails query
2//!
3//! This module exposes [`SearchEmailsQuery`], a structure that helps
4//! you to filter and sort emails. A search emails query is composed
5//! of a [`filter`] query and a [`sort`] query.
6//!
7//! It is actually used by
8//! [`ListEnvelopesOptions`](crate::envelope::list::ListEnvelopesOptions)
9//! to filter and sort envelopes.
10//!
11//! The search emails query can be parsed from a string via
12//! [`FromStr`], see the [`parser`] module for more details.
13//!
14//! ```
15#![doc = include_str!("../../../examples/search_emails_query.rs")]
16//! ```
17
18pub mod error;
19pub mod filter;
20pub mod parser;
21pub mod sort;
22
23use std::str::FromStr;
24
25use error::Error;
26
27use self::{filter::SearchEmailsFilterQuery, sort::SearchEmailsSortQuery};
28
29/// The search emails query structure.
30///
31/// The query is composed of a recursive [`SearchEmailsFilterQuery`]
32/// and a list of [`SearchEmailsSorter`]s.
33#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
34pub struct SearchEmailsQuery {
35    /// The recursive emails search filter query.
36    pub filter: Option<SearchEmailsFilterQuery>,
37
38    /// The emails search sort query.
39    pub sort: Option<SearchEmailsSortQuery>,
40}
41
42/// Parse the given string slice into a [`SearchEmailsQuery`].
43///
44/// Because of the recursive nature of [`SearchEmailsFilterQuery`], it
45/// is not possible to directly parse a full query from a string using
46/// [`chumsky`]. Instead the string is splitted in two, and filters
47/// and sorters are parsed separately.
48///
49/// A search emails query string can contain a filter query, a sorter
50/// query or both. In this last case, the filter query needs to be
51/// defined first, then the sorter query. They should be separated by
52/// the keyword `"order by"`.
53///
54/// See [`filter::parser::query`] for more details on the filter query
55/// string API, and [`sort::parser::query`] for more details on the
56/// sort query API.
57///
58/// # Examples
59///
60/// ```rust
61/// use email::search_query::SearchEmailsQuery;
62/// use std::str::FromStr;
63///
64/// pub fn main() {
65///     // filter only
66///     "subject foo and body bar".parse::<SearchEmailsQuery>().unwrap();
67///
68///     // sort only
69///     "order by date desc".parse::<SearchEmailsQuery>().unwrap();
70///
71///     // filter then sort
72///     "subject foo and body bar order by subject".parse::<SearchEmailsQuery>().unwrap();
73/// }
74/// ```
75///
76/// # ABNF
77///
78/// ```abnf,ignore
79/// query = filter-query / "order by" SP sort-query / filter-query SP "order by" SP sort-query
80#[doc = include_str!("./filter/grammar.abnf")]
81///
82#[doc = include_str!("./sort/grammar.abnf")]
83/// ```
84impl FromStr for SearchEmailsQuery {
85    type Err = Error;
86
87    fn from_str(s: &str) -> Result<Self, Self::Err> {
88        parser::parse(s)
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use crate::search_query::{
95        filter::SearchEmailsFilterQuery,
96        sort::{SearchEmailsSorterKind::*, SearchEmailsSorterOrder::*},
97        SearchEmailsQuery,
98    };
99
100    #[test]
101    fn filters_only() {
102        assert_eq!(
103            "from f and to t".parse::<SearchEmailsQuery>().unwrap(),
104            SearchEmailsQuery {
105                filter: Some(SearchEmailsFilterQuery::And(
106                    Box::new(SearchEmailsFilterQuery::From("f".into())),
107                    Box::new(SearchEmailsFilterQuery::To("t".into()))
108                )),
109                sort: None,
110            },
111        );
112    }
113
114    #[test]
115    fn sorters_only() {
116        assert_eq!(
117            "order by from".parse::<SearchEmailsQuery>().unwrap(),
118            SearchEmailsQuery {
119                filter: None,
120                sort: Some(vec![From.into()])
121            },
122        );
123
124        assert_eq!(
125            "order by from asc subject desc"
126                .parse::<SearchEmailsQuery>()
127                .unwrap(),
128            SearchEmailsQuery {
129                filter: None,
130                sort: Some(vec![From.into(), (Subject, Descending).into()])
131            },
132        );
133    }
134
135    #[test]
136    fn full() {
137        assert_eq!(
138            "from f and to t order by from to desc"
139                .parse::<SearchEmailsQuery>()
140                .unwrap(),
141            SearchEmailsQuery {
142                filter: Some(SearchEmailsFilterQuery::And(
143                    Box::new(SearchEmailsFilterQuery::From("f".into())),
144                    Box::new(SearchEmailsFilterQuery::To("t".into()))
145                )),
146                sort: Some(vec![From.into(), (To, Descending).into()])
147            },
148        );
149    }
150}