Skip to main content

rusmes_core/matchers/
composite.rs

1//! Composite matchers for combining other matchers
2
3use crate::matcher::Matcher;
4use async_trait::async_trait;
5use rusmes_proto::{Mail, MailAddress};
6use std::sync::Arc;
7
8/// Matches when all child matchers match
9pub struct AndMatcher {
10    matchers: Vec<Arc<dyn Matcher>>,
11}
12
13impl AndMatcher {
14    /// Create a new And matcher
15    pub fn new(matchers: Vec<Arc<dyn Matcher>>) -> Self {
16        Self { matchers }
17    }
18}
19
20#[async_trait]
21impl Matcher for AndMatcher {
22    async fn match_mail(&self, mail: &Mail) -> anyhow::Result<Vec<MailAddress>> {
23        if self.matchers.is_empty() {
24            return Ok(Vec::new());
25        }
26
27        // Start with all recipients
28        let mut result: Vec<MailAddress> = mail.recipients().to_vec();
29
30        // Intersect with each matcher's results
31        for matcher in &self.matchers {
32            let matched = matcher.match_mail(mail).await?;
33
34            // Keep only recipients that are in both result and matched
35            result.retain(|r| matched.contains(r));
36
37            // Early exit if no recipients remain
38            if result.is_empty() {
39                break;
40            }
41        }
42
43        Ok(result)
44    }
45
46    fn name(&self) -> &str {
47        "And"
48    }
49}
50
51/// Matches when any child matcher matches
52pub struct OrMatcher {
53    matchers: Vec<Arc<dyn Matcher>>,
54}
55
56impl OrMatcher {
57    /// Create a new Or matcher
58    pub fn new(matchers: Vec<Arc<dyn Matcher>>) -> Self {
59        Self { matchers }
60    }
61}
62
63#[async_trait]
64impl Matcher for OrMatcher {
65    async fn match_mail(&self, mail: &Mail) -> anyhow::Result<Vec<MailAddress>> {
66        let mut result: Vec<MailAddress> = Vec::new();
67
68        // Union of all matched recipients
69        for matcher in &self.matchers {
70            let matched = matcher.match_mail(mail).await?;
71            for recipient in matched {
72                if !result.contains(&recipient) {
73                    result.push(recipient);
74                }
75            }
76        }
77
78        Ok(result)
79    }
80
81    fn name(&self) -> &str {
82        "Or"
83    }
84}
85
86/// Matches when the child matcher does NOT match
87pub struct NotMatcher {
88    matcher: Arc<dyn Matcher>,
89}
90
91impl NotMatcher {
92    /// Create a new Not matcher
93    pub fn new(matcher: Arc<dyn Matcher>) -> Self {
94        Self { matcher }
95    }
96}
97
98#[async_trait]
99impl Matcher for NotMatcher {
100    async fn match_mail(&self, mail: &Mail) -> anyhow::Result<Vec<MailAddress>> {
101        let matched = self.matcher.match_mail(mail).await?;
102
103        // Return recipients NOT in the matched set
104        let result: Vec<MailAddress> = mail
105            .recipients()
106            .iter()
107            .filter(|r| !matched.contains(r))
108            .cloned()
109            .collect();
110
111        Ok(result)
112    }
113
114    fn name(&self) -> &str {
115        "Not"
116    }
117}