sieve/runtime/tests/
test_spamtest.rs1use crate::{
8 compiler::{
9 grammar::{
10 tests::test_spamtest::{TestSpamTest, TestVirusTest},
11 MatchType,
12 },
13 Number,
14 },
15 runtime::Variable,
16 Context, SpamStatus, VirusStatus,
17};
18
19use super::TestResult;
20
21impl TestSpamTest {
22 pub(crate) fn exec(&self, ctx: &mut Context) -> TestResult {
23 let status = if self.percent {
24 ctx.spam_status.as_percentage()
25 } else {
26 ctx.spam_status.as_number()
27 };
28 let value = ctx.eval_value(&self.value);
29 let mut captured_values = Vec::new();
30
31 let result = match &self.match_type {
32 MatchType::Is => self.comparator.is(&status, &value),
33 MatchType::Contains => self
34 .comparator
35 .contains(status.to_string().as_ref(), value.to_string().as_ref()),
36 MatchType::Value(rel_match) => self.comparator.relational(rel_match, &status, &value),
37 MatchType::Matches(capture_positions) => self.comparator.matches(
38 status.to_string().as_ref(),
39 value.to_string().as_ref(),
40 *capture_positions,
41 &mut captured_values,
42 ),
43 MatchType::Regex(capture_positions) => self.comparator.regex(
44 &self.value,
45 &value,
46 status.to_string().as_ref(),
47 *capture_positions,
48 &mut captured_values,
49 ),
50 MatchType::Count(rel_match) => rel_match.cmp(
51 &Number::from(if matches!(&ctx.spam_status, SpamStatus::Unknown) {
52 0.0
53 } else {
54 1.1
55 }),
56 &value.to_number(),
57 ),
58 MatchType::List => false,
59 };
60
61 if !captured_values.is_empty() {
62 ctx.set_match_variables(captured_values);
63 }
64
65 TestResult::Bool(result ^ self.is_not)
66 }
67}
68
69impl TestVirusTest {
70 pub(crate) fn exec(&self, ctx: &mut Context) -> TestResult {
71 let status = ctx.virus_status.as_number();
72 let value = ctx.eval_value(&self.value);
73 let mut captured_values = Vec::new();
74
75 let result = match &self.match_type {
76 MatchType::Is => self.comparator.is(&status, &value),
77 MatchType::Contains => self
78 .comparator
79 .contains(status.to_string().as_ref(), value.to_string().as_ref()),
80 MatchType::Value(rel_match) => self.comparator.relational(rel_match, &status, &value),
81 MatchType::Matches(capture_positions) => self.comparator.matches(
82 status.to_string().as_ref(),
83 value.to_string().as_ref(),
84 *capture_positions,
85 &mut captured_values,
86 ),
87 MatchType::Regex(capture_positions) => self.comparator.regex(
88 &self.value,
89 &value,
90 status.to_string().as_ref(),
91 *capture_positions,
92 &mut captured_values,
93 ),
94 MatchType::Count(rel_match) => rel_match.cmp(
95 &Number::from(if matches!(&ctx.virus_status, VirusStatus::Unknown) {
96 0.0
97 } else {
98 1.1
99 }),
100 &value.to_number(),
101 ),
102 MatchType::List => false,
103 };
104
105 if !captured_values.is_empty() {
106 ctx.set_match_variables(captured_values);
107 }
108
109 TestResult::Bool(result ^ self.is_not)
110 }
111}
112
113impl SpamStatus {
114 pub fn from_number(number: u32) -> Self {
115 match number {
116 1 => SpamStatus::Ham,
117 2..=9 => SpamStatus::MaybeSpam(number as f64 / 10.0),
118 10 => SpamStatus::Spam,
119 _ => SpamStatus::Unknown,
120 }
121 }
122
123 pub(crate) fn as_number(&self) -> Variable {
124 Variable::Integer(match self {
125 SpamStatus::Unknown => 0,
126 SpamStatus::Ham => 1,
127 SpamStatus::MaybeSpam(pct) => ((pct * 10.0) as i64).clamp(2, 9),
128 SpamStatus::Spam => 10,
129 })
130 }
131
132 pub(crate) fn as_percentage(&self) -> Variable {
133 Variable::Integer(match self {
134 SpamStatus::Unknown | SpamStatus::Ham => 0,
135 SpamStatus::MaybeSpam(pct) => ((pct * 100.0).ceil() as i64).clamp(1, 100),
136 SpamStatus::Spam => 100,
137 })
138 }
139}
140
141impl VirusStatus {
142 pub fn from_number(number: u32) -> Self {
143 match number {
144 1 => VirusStatus::Clean,
145 2 => VirusStatus::Replaced,
146 3 => VirusStatus::Cured,
147 4 => VirusStatus::MaybeVirus,
148 5 => VirusStatus::Virus,
149 _ => VirusStatus::Unknown,
150 }
151 }
152
153 pub(crate) fn as_number(&self) -> Variable {
154 Variable::Integer(match self {
155 VirusStatus::Unknown => 0,
156 VirusStatus::Clean => 1,
157 VirusStatus::Replaced => 2,
158 VirusStatus::Cured => 3,
159 VirusStatus::MaybeVirus => 4,
160 VirusStatus::Virus => 5,
161 })
162 }
163}
164
165impl From<u32> for SpamStatus {
166 fn from(number: u32) -> Self {
167 SpamStatus::from_number(number)
168 }
169}
170
171impl From<i32> for SpamStatus {
172 fn from(number: i32) -> Self {
173 SpamStatus::from_number(number as u32)
174 }
175}
176
177impl From<usize> for SpamStatus {
178 fn from(number: usize) -> Self {
179 SpamStatus::from_number(number as u32)
180 }
181}
182
183impl From<u32> for VirusStatus {
184 fn from(number: u32) -> Self {
185 VirusStatus::from_number(number)
186 }
187}
188
189impl From<i32> for VirusStatus {
190 fn from(number: i32) -> Self {
191 VirusStatus::from_number(number as u32)
192 }
193}
194
195impl From<usize> for VirusStatus {
196 fn from(number: usize) -> Self {
197 VirusStatus::from_number(number as u32)
198 }
199}