use serde::{Deserialize, Serialize};
use crate::error::FilterError;
pub trait Filterable {
fn from(&self) -> &str;
fn to(&self) -> &str;
fn cc(&self) -> &str;
fn subject(&self) -> &str;
fn body(&self) -> &str;
fn has_attachment(&self) -> bool;
fn header(&self, name: &str) -> Option<&str> {
let _ = name;
None
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FilterRule {
pub id: String,
pub name: String,
pub enabled: bool,
pub priority: i32,
pub conditions: Vec<Condition>,
pub condition_logic: LogicOp,
pub actions: Vec<Action>,
}
impl FilterRule {
pub fn validate(&self) -> Result<(), FilterError> {
if self.id.is_empty() {
return Err(FilterError::EmptyRuleId);
}
if self.name.is_empty() {
return Err(FilterError::EmptyRuleName);
}
for condition in &self.conditions {
if condition.operator == Operator::Regex {
regex::Regex::new(&condition.value).map_err(|source| FilterError::InvalidRegex {
pattern: condition.value.clone(),
source,
})?;
}
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum LogicOp {
And,
Or,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Condition {
pub field: ConditionField,
pub operator: Operator,
pub value: String,
pub negate: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConditionField {
From,
To,
Cc,
Subject,
Body,
Header(String),
HasAttachment,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Operator {
Contains,
Equals,
Matches,
Regex,
Exists,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Flag {
Seen,
Answered,
Flagged,
Deleted,
Draft,
Keyword(String),
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Action {
MoveTo(String),
CopyTo(String),
Flag(Vec<Flag>),
MarkRead,
Delete,
Forward(String),
}
#[derive(Clone, Debug, Default)]
pub struct FieldValues<'a> {
pub from: &'a str,
pub to: &'a str,
pub cc: &'a str,
pub subject: &'a str,
pub body: &'a str,
pub has_attachment: bool,
pub headers: Vec<(&'a str, &'a str)>,
}
impl Filterable for FieldValues<'_> {
fn from(&self) -> &str {
self.from
}
fn to(&self) -> &str {
self.to
}
fn cc(&self) -> &str {
self.cc
}
fn subject(&self) -> &str {
self.subject
}
fn body(&self) -> &str {
self.body
}
fn has_attachment(&self) -> bool {
self.has_attachment
}
fn header(&self, name: &str) -> Option<&str> {
self.headers
.iter()
.find(|(k, _)| k.eq_ignore_ascii_case(name))
.map(|(_, v)| *v)
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct MailEnvelope {
pub from: String,
pub to: String,
pub cc: String,
pub subject: String,
pub body: String,
pub has_attachment: bool,
pub headers: Vec<(String, String)>,
}
impl Filterable for MailEnvelope {
fn from(&self) -> &str {
&self.from
}
fn to(&self) -> &str {
&self.to
}
fn cc(&self) -> &str {
&self.cc
}
fn subject(&self) -> &str {
&self.subject
}
fn body(&self) -> &str {
&self.body
}
fn has_attachment(&self) -> bool {
self.has_attachment
}
fn header(&self, name: &str) -> Option<&str> {
self.headers
.iter()
.find(|(k, _)| k.eq_ignore_ascii_case(name))
.map(|(_, v)| v.as_str())
}
}