sieve/compiler/grammar/actions/
action_redirect.rs1use crate::compiler::{
8 grammar::{
9 instruction::{CompilerState, Instruction, MapLocalVars},
10 Capability,
11 },
12 lexer::{word::Word, Token},
13 CompileError, Value,
14};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17#[cfg_attr(
18 any(test, feature = "serde"),
19 derive(serde::Serialize, serde::Deserialize)
20)]
21#[cfg_attr(
22 feature = "rkyv",
23 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
24)]
25pub(crate) struct Redirect {
26 pub copy: bool,
27 pub address: Value,
28 pub notify: Notify,
29 pub return_of_content: Ret,
30 pub by_time: ByTime<Value>,
31 pub list: bool,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Hash)]
35#[cfg_attr(
36 any(test, feature = "serde"),
37 derive(serde::Serialize, serde::Deserialize)
38)]
39#[cfg_attr(
40 feature = "rkyv",
41 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
42)]
43pub enum NotifyItem {
44 Success,
45 Failure,
46 Delay,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Hash)]
50#[cfg_attr(
51 any(test, feature = "serde"),
52 derive(serde::Serialize, serde::Deserialize)
53)]
54#[cfg_attr(
55 feature = "rkyv",
56 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
57)]
58pub enum Notify {
59 Never,
60 Items(Vec<NotifyItem>),
61 Default,
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Hash)]
65#[cfg_attr(
66 any(test, feature = "serde"),
67 derive(serde::Serialize, serde::Deserialize)
68)]
69#[cfg_attr(
70 feature = "rkyv",
71 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
72)]
73pub enum Ret {
74 Full,
75 Hdrs,
76 Default,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Hash)]
89#[cfg_attr(
90 any(test, feature = "serde"),
91 derive(serde::Serialize, serde::Deserialize)
92)]
93#[cfg_attr(
94 feature = "rkyv",
95 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
96)]
97pub enum ByTime<T> {
98 Relative {
99 rlimit: u64,
100 mode: ByMode,
101 trace: bool,
102 },
103 Absolute {
104 alimit: T,
105 mode: ByMode,
106 trace: bool,
107 },
108 None,
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Hash)]
112#[cfg_attr(
113 any(test, feature = "serde"),
114 derive(serde::Serialize, serde::Deserialize)
115)]
116#[cfg_attr(
117 feature = "rkyv",
118 derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)
119)]
120pub enum ByMode {
121 Notify,
122 Return,
123 Default,
124}
125
126impl CompilerState<'_> {
127 pub(crate) fn parse_redirect(&mut self) -> Result<(), CompileError> {
128 let address;
129 let mut copy = false;
130 let mut ret = Ret::Default;
131 let mut notify = Notify::Default;
132 let mut list = false;
133 let mut by_mode = ByMode::Default;
134 let mut by_trace = false;
135 let mut by_rlimit = None;
136 let mut by_alimit = None;
137
138 loop {
139 let token_info = self.tokens.unwrap_next()?;
140 match token_info.token {
141 Token::Tag(Word::Copy) => {
142 self.validate_argument(
143 1,
144 Capability::Copy.into(),
145 token_info.line_num,
146 token_info.line_pos,
147 )?;
148 copy = true;
149 }
150 Token::Tag(Word::List) => {
151 self.validate_argument(
152 2,
153 Capability::ExtLists.into(),
154 token_info.line_num,
155 token_info.line_pos,
156 )?;
157 list = true;
158 }
159 Token::Tag(Word::ByTrace) => {
160 self.validate_argument(
161 3,
162 Capability::RedirectDeliverBy.into(),
163 token_info.line_num,
164 token_info.line_pos,
165 )?;
166 by_trace = true;
167 }
168 Token::Tag(Word::ByMode) => {
169 self.validate_argument(
170 4,
171 Capability::RedirectDeliverBy.into(),
172 token_info.line_num,
173 token_info.line_pos,
174 )?;
175 let by_mode_ = self.tokens.expect_static_string()?;
176 if by_mode_.eq_ignore_ascii_case("notify") {
177 by_mode = ByMode::Notify;
178 } else if by_mode_.eq_ignore_ascii_case("return") {
179 by_mode = ByMode::Return;
180 } else {
181 return Err(token_info.expected("\"notify\" or \"return\""));
182 }
183 }
184 Token::Tag(Word::ByTimeRelative) => {
185 self.validate_argument(
186 5,
187 Capability::RedirectDeliverBy.into(),
188 token_info.line_num,
189 token_info.line_pos,
190 )?;
191 by_rlimit = (self.tokens.expect_number(u64::MAX as usize)? as u64).into();
192 }
193 Token::Tag(Word::ByTimeAbsolute) => {
194 self.validate_argument(
195 5,
196 Capability::RedirectDeliverBy.into(),
197 token_info.line_num,
198 token_info.line_pos,
199 )?;
200 by_alimit = self.parse_string()?.into();
201 }
202 Token::Tag(Word::Ret) => {
203 self.validate_argument(
204 6,
205 Capability::RedirectDsn.into(),
206 token_info.line_num,
207 token_info.line_pos,
208 )?;
209 let ret_ = self.tokens.expect_static_string()?;
210 if ret_.eq_ignore_ascii_case("full") {
211 ret = Ret::Full;
212 } else if ret_.eq_ignore_ascii_case("hdrs") {
213 ret = Ret::Hdrs;
214 } else {
215 return Err(token_info.expected("\"FULL\" or \"HDRS\""));
216 }
217 }
218 Token::Tag(Word::Notify) => {
219 self.validate_argument(
220 7,
221 Capability::RedirectDsn.into(),
222 token_info.line_num,
223 token_info.line_pos,
224 )?;
225 let notify_ = self.tokens.expect_static_string()?;
226 if notify_.eq_ignore_ascii_case("never") {
227 notify = Notify::Never;
228 } else {
229 let mut items = Vec::new();
230 for item in notify_.split(',') {
231 let item = item.trim();
232 if item.eq_ignore_ascii_case("success") {
233 items.push(NotifyItem::Success);
234 } else if item.eq_ignore_ascii_case("failure") {
235 items.push(NotifyItem::Failure);
236 } else if item.eq_ignore_ascii_case("delay") {
237 items.push(NotifyItem::Delay);
238 }
239 }
240 if !items.is_empty() {
241 notify = Notify::Items(items);
242 } else {
243 return Err(
244 token_info.expected("\"NEVER\" or \"SUCCESS, FAILURE, DELAY, ..\"")
245 );
246 }
247 }
248 }
249 _ => {
250 address = self.parse_string_token(token_info)?;
251 break;
252 }
253 }
254 }
255
256 self.instructions.push(Instruction::Redirect(Redirect {
257 address,
258 copy,
259 notify,
260 return_of_content: ret,
261 by_time: if let Some(alimit) = by_alimit {
262 ByTime::Absolute {
263 alimit,
264 mode: by_mode,
265 trace: by_trace,
266 }
267 } else if let Some(rlimit) = by_rlimit {
268 ByTime::Relative {
269 rlimit,
270 mode: by_mode,
271 trace: by_trace,
272 }
273 } else {
274 ByTime::None
275 },
276 list,
277 }));
278 Ok(())
279 }
280}
281
282impl MapLocalVars for ByTime<Value> {
283 fn map_local_vars(&mut self, last_id: usize) {
284 if let ByTime::Absolute { alimit, .. } = self {
285 alimit.map_local_vars(last_id)
286 }
287 }
288}