sieve/compiler/grammar/actions/
action_set.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::{
27    compiler::{
28        grammar::{
29            expr::Expression,
30            instruction::{CompilerState, Instruction},
31        },
32        lexer::{tokenizer::TokenInfo, word::Word, Token},
33        CompileError, ErrorType, Value, VariableType,
34    },
35    Envelope,
36};
37
38#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39pub enum Modifier {
40    Lower,
41    Upper,
42    LowerFirst,
43    UpperFirst,
44    QuoteWildcard,
45    QuoteRegex,
46    EncodeUrl,
47    Length,
48    Replace { find: Value, replace: Value },
49}
50
51impl Modifier {
52    pub fn order(&self) -> usize {
53        match self {
54            Modifier::Lower => 41,
55            Modifier::Upper => 40,
56            Modifier::LowerFirst => 31,
57            Modifier::UpperFirst => 30,
58            Modifier::QuoteWildcard => 20,
59            Modifier::QuoteRegex => 21,
60            Modifier::EncodeUrl => 15,
61            Modifier::Length => 10,
62            Modifier::Replace { .. } => 40,
63        }
64    }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
68pub struct Set {
69    pub modifiers: Vec<Modifier>,
70    pub name: VariableType,
71    pub value: Value,
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
75pub(crate) struct Let {
76    pub name: VariableType,
77    pub expr: Vec<Expression>,
78}
79
80impl<'x> CompilerState<'x> {
81    pub(crate) fn parse_set(&mut self) -> Result<(), CompileError> {
82        let mut modifiers = Vec::new();
83        let mut name = None;
84        let mut is_local = false;
85        let value;
86
87        loop {
88            let token_info = self.tokens.unwrap_next()?;
89            match token_info.token {
90                Token::Tag(
91                    word @ (Word::Lower
92                    | Word::Upper
93                    | Word::LowerFirst
94                    | Word::UpperFirst
95                    | Word::QuoteWildcard
96                    | Word::QuoteRegex
97                    | Word::Length
98                    | Word::EncodeUrl),
99                ) => {
100                    let modifier = word.into();
101                    if !modifiers.contains(&modifier) {
102                        modifiers.push(modifier);
103                    }
104                }
105                Token::Tag(Word::Replace) => {
106                    let find = self.tokens.unwrap_next()?;
107                    let replace = self.tokens.unwrap_next()?;
108                    modifiers.push(Modifier::Replace {
109                        find: self.parse_string_token(find)?,
110                        replace: self.parse_string_token(replace)?,
111                    });
112                }
113                Token::Tag(Word::Local) => {
114                    is_local = true;
115                }
116                _ => {
117                    if name.is_none() {
118                        name = self.parse_variable_name(token_info, is_local)?.into();
119                    } else {
120                        value = self.parse_string_token(token_info)?;
121                        break;
122                    }
123                }
124            }
125        }
126
127        modifiers.sort_unstable_by_key(|m| std::cmp::Reverse(m.order()));
128
129        self.instructions.push(Instruction::Set(Set {
130            modifiers,
131            name: name.unwrap(),
132            value,
133        }));
134        Ok(())
135    }
136
137    pub(crate) fn parse_let(&mut self) -> Result<(), CompileError> {
138        let name = self.tokens.unwrap_next()?;
139        let name = self.parse_variable_name(name, false)?;
140        let expr = self.parse_expr()?;
141
142        self.instructions.push(Instruction::Let(Let { name, expr }));
143        Ok(())
144    }
145
146    pub(crate) fn parse_variable_name(
147        &mut self,
148        token_info: TokenInfo,
149        register_as_local: bool,
150    ) -> Result<VariableType, CompileError> {
151        match token_info.token {
152            Token::StringConstant(value) => self
153                .register_variable(value.into_string(), register_as_local)
154                .map_err(|error_type| CompileError {
155                    line_num: token_info.line_num,
156                    line_pos: token_info.line_pos,
157                    error_type,
158                }),
159            _ => Err(token_info.custom(ErrorType::ExpectedConstantString)),
160        }
161    }
162
163    pub(crate) fn register_variable(
164        &mut self,
165        name: String,
166        register_as_local: bool,
167    ) -> Result<VariableType, ErrorType> {
168        let name = name.to_lowercase();
169        if let Some((namespace, part)) = name.split_once('.') {
170            match namespace {
171                "global" | "t" => Ok(VariableType::Global(part.to_string())),
172                "envelope" => Envelope::try_from(part)
173                    .map(VariableType::Envelope)
174                    .map_err(|_| ErrorType::InvalidNamespace(namespace.to_string())),
175                _ => Err(ErrorType::InvalidNamespace(namespace.to_string())),
176            }
177        } else {
178            Ok(if !self.is_var_global(&name) {
179                VariableType::Local(self.register_local_var(name, register_as_local))
180            } else {
181                VariableType::Global(name)
182            })
183        }
184    }
185}
186
187impl From<Word> for Modifier {
188    fn from(word: Word) -> Self {
189        match word {
190            Word::Lower => Modifier::Lower,
191            Word::Upper => Modifier::Upper,
192            Word::LowerFirst => Modifier::LowerFirst,
193            Word::UpperFirst => Modifier::UpperFirst,
194            Word::QuoteWildcard => Modifier::QuoteWildcard,
195            Word::QuoteRegex => Modifier::QuoteRegex,
196            Word::Length => Modifier::Length,
197            Word::EncodeUrl => Modifier::EncodeUrl,
198            _ => unreachable!(),
199        }
200    }
201}