use regex::Regex;
use std::{borrow::Cow, convert::Infallible};
use super::{Filter, FilterableEntries};
use crate::{actions::transforms::field::Field, error::BadRegexError};
#[derive(Clone, Debug)]
pub struct Contains {
pub re: Regex,
pub field: Field,
}
impl Contains {
pub fn new(regex: &str, field: Field) -> Result<Self, BadRegexError> {
Ok(Self {
re: Regex::new(regex)?,
field,
})
}
}
impl Filter for Contains {
type Err = Infallible;
async fn filter(&mut self, mut entries: FilterableEntries<'_>) -> Result<(), Self::Err> {
entries.retain(|ent| {
let field = match self.field {
Field::Title => ent.msg.title.as_deref().map(Cow::Borrowed),
Field::Body => ent.msg.body.as_deref().map(Cow::Borrowed),
Field::Link => ent.msg.link.as_ref().map(|s| Cow::Owned(s.clone())),
Field::Id => ent.id.as_ref().map(|id| Cow::Borrowed(id.0.as_str())),
Field::ReplyTo => ent.reply_to.as_ref().map(|id| Cow::Borrowed(id.0.as_str())),
Field::RawContents => ent.raw_contents.as_deref().map(Cow::Borrowed),
};
field.is_some_and(|field| self.re.is_match(&field))
});
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::{
actions::{
filters::{Filter, FilterableEntries},
transforms::field::Field,
},
entry::Entry,
sinks::message::Message,
};
use super::Contains;
#[tokio::test]
async fn contains() {
let bodies = [
"Hello, World!",
"Hello, Earth!",
"Bye, World!",
"Bye, Earth!",
];
let mut entries = bodies
.into_iter()
.map(|body| {
Entry::builder()
.msg(Message::builder().body(body.to_owned()))
.build()
})
.collect::<Vec<_>>();
let mut contains = Contains::new("World", Field::Body).unwrap();
contains
.filter(FilterableEntries::new(&mut entries))
.await
.unwrap();
assert_eq!(
entries
.iter()
.map(|ent| ent.msg.body.as_ref().unwrap().as_str())
.collect::<Vec<_>>(),
["Hello, World!", "Bye, World!"]
);
}
}