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