sieve/compiler/grammar/actions/
action_require.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::compiler::{
8    grammar::{
9        instruction::{CompilerState, Instruction},
10        Capability,
11    },
12    lexer::Token,
13    CompileError,
14};
15
16impl CompilerState<'_> {
17    fn add_capability(&mut self, capabilities: &mut Vec<Capability>, capability: Capability) {
18        if !self.has_capability(&capability) {
19            let parent_capability = if matches!(&capability, Capability::SpamTestPlus) {
20                Some(Capability::SpamTest)
21            } else {
22                None
23            };
24            capabilities.push(capability.clone());
25            self.block.capabilities.insert(capability);
26
27            if let Some(capability) = parent_capability {
28                if !self.has_capability(&capability) {
29                    capabilities.push(capability.clone());
30                    self.block.capabilities.insert(capability);
31                }
32            }
33        }
34    }
35
36    pub(crate) fn parse_require(&mut self) -> Result<(), CompileError> {
37        let mut capabilities = Vec::new();
38
39        let token_info = self.tokens.unwrap_next()?;
40        match token_info.token {
41            Token::BracketOpen => loop {
42                let token_info = self.tokens.unwrap_next()?;
43                match token_info.token {
44                    Token::StringConstant(value) => {
45                        self.add_capability(
46                            &mut capabilities,
47                            Capability::parse(value.to_string().as_ref()),
48                        );
49                        let token_info = self.tokens.unwrap_next()?;
50                        match token_info.token {
51                            Token::Comma => (),
52                            Token::BracketClose => break,
53                            _ => {
54                                return Err(token_info.expected("']' or ','"));
55                            }
56                        }
57                    }
58                    _ => {
59                        return Err(token_info.expected("string"));
60                    }
61                }
62            },
63            Token::StringConstant(value) => {
64                self.add_capability(
65                    &mut capabilities,
66                    Capability::parse(value.to_string().as_ref()),
67                );
68            }
69            _ => {
70                return Err(token_info.expected("'[' or string"));
71            }
72        }
73
74        if !capabilities.is_empty() {
75            if self.block.require_pos == usize::MAX {
76                self.block.require_pos = self.instructions.len();
77                self.instructions.push(Instruction::Require(capabilities));
78            } else if let Some(Instruction::Require(capabilties)) =
79                self.instructions.get_mut(self.block.require_pos)
80            {
81                capabilties.extend(capabilities)
82            } else {
83                #[cfg(test)]
84                panic!(
85                    "Invalid require instruction position {}.",
86                    self.block.require_pos
87                )
88            }
89        }
90
91        Ok(())
92    }
93}