sieve/compiler/grammar/tests/
test_envelope.rs1use phf::phf_map;
25use serde::{Deserialize, Serialize};
26
27use crate::{
28 compiler::{
29 grammar::{instruction::CompilerState, Capability, Comparator},
30 lexer::{word::Word, Token},
31 CompileError, ErrorType, Value,
32 },
33 Envelope,
34};
35
36use crate::compiler::grammar::{test::Test, AddressPart, MatchType};
37
38#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39pub(crate) struct TestEnvelope {
40 pub envelope_list: Vec<Envelope>,
41 pub key_list: Vec<Value>,
42 pub address_part: AddressPart,
43 pub match_type: MatchType,
44 pub comparator: Comparator,
45 pub zone: Option<i64>,
46 pub is_not: bool,
47}
48
49impl<'x> CompilerState<'x> {
50 pub(crate) fn parse_test_envelope(&mut self) -> Result<Test, CompileError> {
51 let mut address_part = AddressPart::All;
52 let mut match_type = MatchType::Is;
53 let mut comparator = Comparator::AsciiCaseMap;
54 let mut envelope_list = None;
55 let mut key_list;
56 let mut zone = None;
57
58 loop {
59 let mut token_info = self.tokens.unwrap_next()?;
60 match token_info.token {
61 Token::Tag(
62 word @ (Word::LocalPart | Word::Domain | Word::All | Word::User | Word::Detail),
63 ) => {
64 self.validate_argument(
65 1,
66 if matches!(word, Word::User | Word::Detail) {
67 Capability::SubAddress.into()
68 } else {
69 None
70 },
71 token_info.line_num,
72 token_info.line_pos,
73 )?;
74 address_part = word.into();
75 }
76 Token::Tag(
77 word @ (Word::Is
78 | Word::Contains
79 | Word::Matches
80 | Word::Value
81 | Word::Count
82 | Word::Regex
83 | Word::List),
84 ) => {
85 self.validate_argument(
86 2,
87 match word {
88 Word::Value | Word::Count => Capability::Relational.into(),
89 Word::Regex => Capability::Regex.into(),
90 Word::List => Capability::ExtLists.into(),
91 _ => None,
92 },
93 token_info.line_num,
94 token_info.line_pos,
95 )?;
96
97 match_type = self.parse_match_type(word)?;
98 }
99 Token::Tag(Word::Comparator) => {
100 self.validate_argument(3, None, token_info.line_num, token_info.line_pos)?;
101 comparator = self.parse_comparator()?;
102 }
103 Token::Tag(Word::Zone) => {
104 self.validate_argument(
105 4,
106 Capability::EnvelopeDeliverBy.into(),
107 token_info.line_num,
108 token_info.line_pos,
109 )?;
110 zone = self.parse_timezone()?.into();
111 }
112 _ => {
113 if envelope_list.is_none() {
114 let mut envelopes = Vec::new();
115 let line_num = token_info.line_num;
116 let line_pos = token_info.line_pos;
117
118 match token_info.token {
119 Token::StringConstant(s) => match Envelope::try_from(s.into_string()) {
120 Ok(envelope) => {
121 envelopes.push(envelope);
122 }
123 Err(invalid) => {
124 token_info.token = Token::Comma;
125 return Err(
126 token_info.custom(ErrorType::InvalidEnvelope(invalid))
127 );
128 }
129 },
130 Token::BracketOpen => loop {
131 let mut token_info = self.tokens.unwrap_next()?;
132 match token_info.token {
133 Token::StringConstant(s) => {
134 match Envelope::try_from(s.into_string()) {
135 Ok(envelope) => {
136 if !envelopes.contains(&envelope) {
137 envelopes.push(envelope);
138 }
139 }
140 Err(invalid) => {
141 token_info.token = Token::Comma;
142 return Err(token_info
143 .custom(ErrorType::InvalidEnvelope(invalid)));
144 }
145 }
146 }
147 Token::Comma => (),
148 Token::BracketClose if !envelopes.is_empty() => break,
149 _ => return Err(token_info.expected("constant string")),
150 }
151 },
152 _ => return Err(token_info.expected("constant string")),
153 }
154
155 for envelope in &envelopes {
156 match envelope {
157 Envelope::ByTimeAbsolute
158 | Envelope::ByTimeRelative
159 | Envelope::ByMode
160 | Envelope::ByTrace => {
161 self.validate_argument(
162 0,
163 Capability::EnvelopeDeliverBy.into(),
164 line_num,
165 line_pos,
166 )?;
167 }
168
169 Envelope::Notify
170 | Envelope::Orcpt
171 | Envelope::Ret
172 | Envelope::Envid => {
173 self.validate_argument(
174 0,
175 Capability::EnvelopeDsn.into(),
176 line_num,
177 line_pos,
178 )?;
179 }
180 _ => (),
181 }
182 }
183
184 envelope_list = envelopes.into();
185 } else {
186 key_list = self.parse_strings_token(token_info)?;
187 break;
188 }
189 }
190 }
191 }
192 self.validate_match(&match_type, &mut key_list)?;
193
194 Ok(Test::Envelope(TestEnvelope {
195 envelope_list: envelope_list.unwrap(),
196 key_list,
197 address_part,
198 match_type,
199 comparator,
200 zone,
201 is_not: false,
202 }))
203 }
204}
205
206impl TryFrom<String> for Envelope {
207 type Error = String;
208
209 fn try_from(value: String) -> Result<Self, Self::Error> {
210 if let Some(envelope) = ENVELOPE.get(&value) {
211 Ok(*envelope)
212 } else {
213 Err(value)
214 }
215 }
216}
217
218impl<'x> TryFrom<&'x str> for Envelope {
219 type Error = &'x str;
220
221 fn try_from(value: &'x str) -> Result<Self, Self::Error> {
222 if let Some(envelope) = ENVELOPE.get(value) {
223 Ok(*envelope)
224 } else {
225 Err(value)
226 }
227 }
228}
229
230pub(crate) static ENVELOPE: phf::Map<&'static str, Envelope> = phf_map! {
231 "from" => Envelope::From,
232 "to" => Envelope::To,
233 "bytimeabsolute" => Envelope::ByTimeAbsolute,
234 "bytimerelative" => Envelope::ByTimeRelative,
235 "bymode" => Envelope::ByMode,
236 "bytrace" => Envelope::ByTrace,
237 "notify" => Envelope::Notify,
238 "orcpt" => Envelope::Orcpt,
239 "ret" => Envelope::Ret,
240 "envid" => Envelope::Envid,
241};