sieve/compiler/grammar/tests/
test_string.rs

1/*
2 * Copyright (c) 2020-2023, Stalwart Labs Ltd.
3 *
4 * This file is part of the Stalwart Sieve Interpreter.
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, either version 3 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 * in the LICENSE file at the top-level directory of this distribution.
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 *
19 * You can be released from the requirements of the AGPLv3 license by
20 * purchasing a commercial license. Please contact licensing@stalw.art
21 * for more details.
22*/
23
24use serde::{Deserialize, Serialize};
25
26use crate::compiler::{
27    grammar::{instruction::CompilerState, Capability, Comparator},
28    lexer::{word::Word, Token},
29    CompileError, Value,
30};
31
32use crate::compiler::grammar::{test::Test, MatchType};
33
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35pub struct TestString {
36    pub match_type: MatchType,
37    pub comparator: Comparator,
38    pub source: Vec<Value>,
39    pub key_list: Vec<Value>,
40    pub is_not: bool,
41}
42
43impl<'x> CompilerState<'x> {
44    pub(crate) fn parse_test_string(&mut self) -> Result<Test, CompileError> {
45        let mut match_type = MatchType::Is;
46        let mut comparator = Comparator::AsciiCaseMap;
47        let mut source = None;
48        let mut key_list: Vec<Value>;
49
50        loop {
51            let token_info = self.tokens.unwrap_next()?;
52            match token_info.token {
53                Token::Tag(
54                    word @ (Word::Is
55                    | Word::Contains
56                    | Word::Matches
57                    | Word::Value
58                    | Word::Count
59                    | Word::Regex
60                    | Word::List),
61                ) => {
62                    self.validate_argument(
63                        1,
64                        match word {
65                            Word::Value | Word::Count => Capability::Relational.into(),
66                            Word::Regex => Capability::Regex.into(),
67                            Word::List => Capability::ExtLists.into(),
68                            _ => None,
69                        },
70                        token_info.line_num,
71                        token_info.line_pos,
72                    )?;
73
74                    match_type = self.parse_match_type(word)?;
75                }
76                Token::Tag(Word::Comparator) => {
77                    self.validate_argument(2, None, token_info.line_num, token_info.line_pos)?;
78                    comparator = self.parse_comparator()?;
79                }
80                _ => {
81                    if source.is_none() {
82                        source = self.parse_strings_token(token_info)?.into();
83                    } else {
84                        key_list = self.parse_strings_token(token_info)?;
85                        break;
86                    }
87                }
88            }
89        }
90        self.validate_match(&match_type, &mut key_list)?;
91
92        Ok(Test::String(TestString {
93            source: source.unwrap(),
94            key_list,
95            match_type,
96            comparator,
97            is_not: false,
98        }))
99    }
100}