mail_query/options.rs
1use std::collections::HashSet;
2use std::fmt;
3
4use crate::parser::canonical_filter_name;
5
6/// Caller-provided configuration for the parser.
7///
8/// # Custom filters
9///
10/// The built-in filter vocabulary (`is:unread`, `has:attachment`, etc.)
11/// is fixed at compile time. Anything else — `is:owed-reply`,
12/// `has:reaction`, `is:my-app-flag` — returns
13/// [`ParseError::UnknownFilter`][crate::ParseError::UnknownFilter] by
14/// default. Register the names you want to accept so they parse as
15/// [`FilterKind::Custom`][crate::FilterKind::Custom] instead.
16///
17/// ```
18/// use mail_query::{parse_with, FilterKind, ParserOptions, QueryNode};
19///
20/// let mut options = ParserOptions::default();
21/// options.register_custom_filter("owed-reply");
22///
23/// let ast = parse_with("is:owed-reply", &options).expect("parses");
24/// assert_eq!(
25/// ast,
26/// QueryNode::Filter(FilterKind::Custom("owed-reply".into()))
27/// );
28/// ```
29#[derive(Default)]
30pub struct ParserOptions {
31 custom_filters: HashSet<String>,
32}
33
34impl fmt::Debug for ParserOptions {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 f.debug_struct("ParserOptions")
37 .field("custom_filters", &self.custom_filters)
38 .finish()
39 }
40}
41
42impl ParserOptions {
43 /// New empty options. Equivalent to [`Default::default`].
44 pub fn new() -> Self {
45 Self::default()
46 }
47
48 /// Register a custom filter name. The crate canonicalises to
49 /// lowercase + hyphenated form (`Reply_Later` becomes `reply-later`),
50 /// so callers can pass any casing.
51 pub fn register_custom_filter(&mut self, name: impl Into<String>) -> &mut Self {
52 let canonical = canonical_filter_name(&name.into());
53 self.custom_filters.insert(canonical);
54 self
55 }
56
57 /// Register many custom filter names at once.
58 pub fn register_custom_filters<I, S>(&mut self, names: I) -> &mut Self
59 where
60 I: IntoIterator<Item = S>,
61 S: Into<String>,
62 {
63 for name in names {
64 self.register_custom_filter(name);
65 }
66 self
67 }
68
69 /// True if `name` (already canonicalised) is registered.
70 pub(crate) fn has_custom_filter(&self, canonical: &str) -> bool {
71 self.custom_filters.contains(canonical)
72 }
73}