pub fn parse_mailbox_path(path: &str) -> impl Iterator<Item = &str> + '_ {
path.split('/')
.filter(|s| !s.is_empty())
.enumerate()
.map(|(ix, s)| {
if 0 == ix && "inbox".eq_ignore_ascii_case(s) {
"INBOX"
} else {
s
}
})
}
pub fn mailbox_path_matcher<'a>(
patterns: impl IntoIterator<Item = &'a str>,
) -> impl Fn(&str) -> bool + 'a {
let mut rx = "^(".to_owned();
for (pattern_ix, pattern) in patterns.into_iter().enumerate() {
if pattern_ix > 0 {
rx.push('|');
}
for (part_ix, part) in parse_mailbox_path(pattern).enumerate() {
if part_ix > 0 {
rx.push('/');
}
let mut start = 0;
for end in part
.match_indices(['%', '*'])
.map(|(ix, _)| ix)
.chain(part.len()..=part.len())
{
let chunk = &part[start..end];
start = (end + 1).min(part.len());
rx.push_str(®ex::escape(chunk));
match part.get(end..end + 1) {
Some("*") => rx.push_str(".*"),
Some("%") => rx.push_str("[^/]*"),
_ => (),
}
}
}
}
rx.push_str(")$");
let rx = regex::Regex::new(&rx).expect("Built invalid regex?");
move |s| rx.is_match(s)
}
#[cfg(test)]
mod test {
use std::iter;
use super::*;
#[test]
fn test_parse_mailbox_path() {
fn p(p: &'static str) -> Vec<&'static str> {
parse_mailbox_path(p).collect()
}
assert_eq!(vec!["INBOX"], p("inbox"));
assert_eq!(vec!["INBOX", "foo"], p("Inbox/foo"));
assert_eq!(vec!["bar"], p("/bar"));
assert_eq!(vec!["bar"], p("bar/"));
assert_eq!(vec!["foo", "bar"], p("foo//bar"));
assert_eq!(vec!["foo", "InBoX"], p("foo/InBoX"));
}
#[test]
fn test_mailbox_patterns() {
fn matches(pat: &str, mb: &str) -> bool {
mailbox_path_matcher(iter::once(pat))(mb)
}
assert!(matches("*", "INBOX"));
assert!(matches("%", "INBOX"));
assert!(matches("INB*X", "INBOX"));
assert!(matches("INB*X", "INB/BOX"));
assert!(!matches("INB*X", "INBOX/plugh"));
assert!(!matches("INB*X", "foo/INBOX"));
assert!(matches("INB%X", "INBOX"));
assert!(!matches("INB%X", "INB/BOX"));
assert!(!matches("INB%X", "INBOX/plugh"));
assert!(matches("INB*", "INBOX"));
assert!(matches("INB*", "INBOX/plugh"));
assert!(matches("INB%", "INBOX"));
assert!(!matches("INB%", "INBOX/plugh"));
assert!(!matches("INB%", "foo/INBOX"));
assert!(matches("*X", "INBOX"));
assert!(matches("*X", "foo/boX"));
assert!(matches("%X", "INBOX"));
assert!(!matches("%X", "foo/boX"));
assert!(matches("foo/bar", "foo/bar"));
assert!(!matches("foo/bar", "foo/bar/baz"));
assert!(!matches("foo/*", "foo"));
assert!(matches("foo/*", "foo/bar"));
assert!(matches("foo/*", "foo/bar/baz"));
assert!(matches("foo/%", "foo/bar"));
assert!(!matches("foo/%", "foo/bar/baz"));
assert!(matches("inbox", "INBOX"));
}
}