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