sieve/compiler/grammar/tests/
test_duplicate.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 mail_parser::HeaderName;
8
9
10use crate::compiler::{
11    grammar::instruction::{CompilerState, MapLocalVars},
12    lexer::{word::Word, Token},
13    CompileError, ErrorType, Value,
14};
15
16use crate::compiler::grammar::test::Test;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19#[cfg_attr(
20    any(test, feature = "serde"),
21    derive(serde::Serialize, serde::Deserialize)
22)]
23#[cfg_attr(
24    feature = "rkyv",
25    derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
26)]
27pub(crate) struct TestDuplicate {
28    pub handle: Option<Value>,
29    pub dup_match: DupMatch,
30    pub seconds: Option<u64>,
31    pub last: bool,
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) enum DupMatch {
45    Header(Value),
46    UniqueId(Value),
47    Default,
48}
49
50impl CompilerState<'_> {
51    pub(crate) fn parse_test_duplicate(&mut self) -> Result<Test, CompileError> {
52        let mut handle = None;
53        let mut dup_match = DupMatch::Default;
54        let mut seconds = None;
55        let mut last = false;
56
57        while let Some(token_info) = self.tokens.peek() {
58            let token_info = token_info?;
59            let line_num = token_info.line_num;
60            let line_pos = token_info.line_pos;
61
62            match token_info.token {
63                Token::Tag(Word::Handle) => {
64                    self.validate_argument(1, None, line_num, line_pos)?;
65                    self.tokens.next();
66                    handle = self.parse_string()?.into();
67                }
68                Token::Tag(Word::Header) => {
69                    self.validate_argument(2, None, line_num, line_pos)?;
70                    self.tokens.next();
71                    let header = self.parse_string()?;
72                    if let Value::Text(header_name) = &header {
73                        if HeaderName::parse(header_name.as_ref()).is_none() {
74                            return Err(self
75                                .tokens
76                                .unwrap_next()?
77                                .custom(ErrorType::InvalidHeaderName));
78                        }
79                    }
80                    dup_match = DupMatch::Header(header);
81                }
82                Token::Tag(Word::UniqueId) => {
83                    self.validate_argument(2, None, line_num, line_pos)?;
84                    self.tokens.next();
85                    dup_match = DupMatch::UniqueId(self.parse_string()?);
86                }
87                Token::Tag(Word::Seconds) => {
88                    self.validate_argument(3, None, line_num, line_pos)?;
89                    self.tokens.next();
90                    seconds = (self.tokens.expect_number(u64::MAX as usize)? as u64).into();
91                }
92                Token::Tag(Word::Last) => {
93                    self.validate_argument(4, None, line_num, line_pos)?;
94                    self.tokens.next();
95                    last = true;
96                }
97                _ => break,
98            }
99        }
100
101        Ok(Test::Duplicate(TestDuplicate {
102            handle,
103            dup_match,
104            seconds,
105            last,
106            is_not: false,
107        }))
108    }
109}
110
111impl MapLocalVars for DupMatch {
112    fn map_local_vars(&mut self, last_id: usize) {
113        match self {
114            DupMatch::Header(header) => header.map_local_vars(last_id),
115            DupMatch::UniqueId(unique_id) => unique_id.map_local_vars(last_id),
116            DupMatch::Default => {}
117        }
118    }
119}