Skip to main content

sieve/compiler/grammar/tests/
test_mailbox.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
5 */
6
7use crate::{
8    Metadata,
9    compiler::{
10        CompileError, RawValue, Value,
11        grammar::{
12            Capability, Comparator,
13            instruction::{CompilerState, MapLocalVars},
14        },
15        lexer::{Token, word::Word},
16    },
17};
18
19use crate::compiler::grammar::{MatchType, test::Test};
20
21#[derive(Debug, Clone, PartialEq, Eq)]
22#[cfg_attr(
23    any(test, feature = "serde"),
24    derive(serde::Serialize, serde::Deserialize)
25)]
26#[cfg_attr(
27    feature = "rkyv",
28    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
29)]
30pub(crate) struct TestMailboxExists {
31    pub mailbox_names: Box<[Value]>,
32    pub is_not: bool,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
36#[cfg_attr(
37    any(test, feature = "serde"),
38    derive(serde::Serialize, serde::Deserialize)
39)]
40#[cfg_attr(
41    feature = "rkyv",
42    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
43)]
44pub(crate) struct TestMetadataExists {
45    pub mailbox: Option<Value>,
46    pub annotation_names: Box<[Value]>,
47    pub is_not: bool,
48}
49
50/*
51
52metadata [MATCH-TYPE] [COMPARATOR]
53           <mailbox: string>
54           <annotation-name: string> <key-list: string-list>
55
56*/
57
58#[derive(Debug, Clone, PartialEq, Eq)]
59#[cfg_attr(
60    any(test, feature = "serde"),
61    derive(serde::Serialize, serde::Deserialize)
62)]
63#[cfg_attr(
64    feature = "rkyv",
65    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
66)]
67pub(crate) struct TestMetadata {
68    pub match_type: MatchType,
69    pub comparator: Comparator,
70    pub medatata: Metadata<Value>,
71    pub key_list: Box<[Value]>,
72    pub is_not: bool,
73}
74
75/*
76
77servermetadata [MATCH-TYPE] [COMPARATOR]
78           <annotation-name: string> <key-list: string-list>
79
80*/
81
82impl CompilerState<'_> {
83    pub(crate) fn parse_test_mailboxexists(&mut self) -> Result<Test, CompileError> {
84        Ok(Test::MailboxExists(Box::new(TestMailboxExists {
85            mailbox_names: self.parse_strings(false)?.into(),
86            is_not: false,
87        })))
88    }
89
90    pub(crate) fn parse_test_metadataexists(&mut self) -> Result<Test, CompileError> {
91        Ok(Test::MetadataExists(Box::new(TestMetadataExists {
92            mailbox: self.parse_string()?.into(),
93            annotation_names: self.parse_strings(false)?.into(),
94            is_not: false,
95        })))
96    }
97
98    pub(crate) fn parse_test_servermetadataexists(&mut self) -> Result<Test, CompileError> {
99        Ok(Test::MetadataExists(Box::new(TestMetadataExists {
100            mailbox: None,
101            annotation_names: self.parse_strings(false)?.into(),
102            is_not: false,
103        })))
104    }
105
106    pub(crate) fn parse_test_metadata(&mut self) -> Result<Test, CompileError> {
107        let mut match_type = MatchType::Is;
108        let mut comparator = Comparator::AsciiCaseMap;
109        let mut mailbox = None;
110        let mut annotation_name = None;
111        let key_list: Vec<RawValue>;
112
113        loop {
114            let token_info = self.tokens.unwrap_next()?;
115            match token_info.token {
116                Token::Tag(
117                    word @ (Word::Is
118                    | Word::Contains
119                    | Word::Matches
120                    | Word::Value
121                    | Word::Count
122                    | Word::Regex),
123                ) => {
124                    self.validate_argument(
125                        1,
126                        match word {
127                            Word::Value | Word::Count => Capability::Relational.into(),
128                            Word::Regex => Capability::Regex.into(),
129                            Word::List => Capability::ExtLists.into(),
130                            _ => None,
131                        },
132                        token_info.line_num,
133                        token_info.line_pos,
134                    )?;
135
136                    match_type = self.parse_match_type(word)?;
137                }
138                Token::Tag(Word::Comparator) => {
139                    self.validate_argument(2, None, token_info.line_num, token_info.line_pos)?;
140                    comparator = self.parse_comparator()?;
141                }
142                _ => {
143                    if mailbox.is_none() {
144                        mailbox = self.parse_string_token(token_info)?.into();
145                    } else if annotation_name.is_none() {
146                        annotation_name = self.parse_string_token(token_info)?.into();
147                    } else {
148                        key_list = self.parse_raw_strings_token(token_info)?;
149                        break;
150                    }
151                }
152            }
153        }
154        let key_list = self.validate_match(&match_type, &comparator, key_list)?;
155
156        Ok(Test::Metadata(Box::new(TestMetadata {
157            match_type,
158            comparator,
159            medatata: Metadata::Mailbox {
160                name: mailbox.unwrap(),
161                annotation: annotation_name.unwrap(),
162            },
163            key_list: key_list.into(),
164            is_not: false,
165        })))
166    }
167
168    pub(crate) fn parse_test_servermetadata(&mut self) -> Result<Test, CompileError> {
169        let mut match_type = MatchType::Is;
170        let mut comparator = Comparator::AsciiCaseMap;
171        let mut annotation_name = None;
172        let key_list: Vec<RawValue>;
173
174        loop {
175            let token_info = self.tokens.unwrap_next()?;
176            match token_info.token {
177                Token::Tag(
178                    word @ (Word::Is
179                    | Word::Contains
180                    | Word::Matches
181                    | Word::Value
182                    | Word::Count
183                    | Word::Regex),
184                ) => {
185                    self.validate_argument(
186                        1,
187                        match word {
188                            Word::Value | Word::Count => Capability::Relational.into(),
189                            Word::Regex => Capability::Regex.into(),
190                            Word::List => Capability::ExtLists.into(),
191                            _ => None,
192                        },
193                        token_info.line_num,
194                        token_info.line_pos,
195                    )?;
196
197                    match_type = self.parse_match_type(word)?;
198                }
199                Token::Tag(Word::Comparator) => {
200                    self.validate_argument(2, None, token_info.line_num, token_info.line_pos)?;
201                    comparator = self.parse_comparator()?;
202                }
203                _ => {
204                    if annotation_name.is_none() {
205                        annotation_name = self.parse_string_token(token_info)?.into();
206                    } else {
207                        key_list = self.parse_raw_strings_token(token_info)?;
208                        break;
209                    }
210                }
211            }
212        }
213        let key_list = self.validate_match(&match_type, &comparator, key_list)?;
214
215        Ok(Test::Metadata(Box::new(TestMetadata {
216            match_type,
217            comparator,
218            medatata: Metadata::Server {
219                annotation: annotation_name.unwrap(),
220            },
221            key_list: key_list.into(),
222            is_not: false,
223        })))
224    }
225}
226
227impl MapLocalVars for Metadata<Value> {
228    fn map_local_vars(&mut self, last_id: u16) {
229        match self {
230            Metadata::Mailbox { name, annotation } => {
231                name.map_local_vars(last_id);
232                annotation.map_local_vars(last_id);
233            }
234            Metadata::Server { annotation } => {
235                annotation.map_local_vars(last_id);
236            }
237        }
238    }
239}