sieve/compiler/grammar/tests/
test_duplicate.rs1use 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}