Skip to main content

reddb_server/storage/query/parser/
config.rs

1//! Parser for stable CONFIG keyed commands.
2
3use super::super::ast::{ConfigCommand, ConfigValueType, QueryExpr};
4use super::super::lexer::Token;
5use super::error::ParseError;
6use super::Parser;
7
8impl<'a> Parser<'a> {
9    pub fn parse_config_command(&mut self) -> Result<QueryExpr, ParseError> {
10        let operation = self.expect_ident_or_keyword()?.to_ascii_uppercase();
11        if operation != "PUT"
12            && operation != "GET"
13            && operation != "RESOLVE"
14            && operation != "ROTATE"
15            && operation != "DELETE"
16            && operation != "HISTORY"
17            && operation != "LIST"
18            && operation != "WATCH"
19            && operation != "INCR"
20            && operation != "DECR"
21            && operation != "ADD"
22            && operation != "INVALIDATE"
23        {
24            return Err(ParseError::expected(
25                vec![
26                    "PUT",
27                    "GET",
28                    "RESOLVE",
29                    "ROTATE",
30                    "DELETE",
31                    "HISTORY",
32                    "LIST",
33                    "WATCH",
34                    "INCR",
35                    "DECR",
36                    "ADD",
37                    "INVALIDATE",
38                ],
39                self.peek(),
40                self.position(),
41            ));
42        }
43
44        if !self.consume_ident_ci("CONFIG")? {
45            return Err(ParseError::expected(
46                vec!["CONFIG"],
47                self.peek(),
48                self.position(),
49            ));
50        }
51
52        let mut collection = self.expect_ident_or_keyword()?.to_ascii_lowercase();
53        if self.consume(&Token::Dot)? {
54            let next = self.expect_ident_or_keyword()?.to_ascii_lowercase();
55            collection = format!("{collection}.{next}");
56        }
57        let key = if operation == "LIST"
58            || (operation == "WATCH"
59                && matches!(self.peek(), Token::Ident(name) if name.eq_ignore_ascii_case("PREFIX")))
60        {
61            None
62        } else if !matches!(self.peek(), Token::Eof) {
63            Some(self.expect_ident_or_keyword()?.to_ascii_lowercase())
64        } else {
65            None
66        };
67
68        match operation.as_str() {
69            "PUT" => {
70                let key = key.ok_or_else(|| {
71                    ParseError::expected(vec!["config key"], self.peek(), self.position())
72                })?;
73                self.expect(Token::Eq)?;
74                let value = self.parse_value()?;
75                let value_type = self.parse_config_value_type()?;
76                let tags = self.parse_optional_config_tags()?;
77                if self.consume_ident_ci("TTL")? || self.consume_ident_ci("EXPIRE")? {
78                    self.consume_config_tail()?;
79                    return Ok(QueryExpr::ConfigCommand(
80                        ConfigCommand::InvalidVolatileOperation {
81                            operation: "TTL/EXPIRE".to_string(),
82                            collection,
83                            key: Some(key),
84                        },
85                    ));
86                }
87                Ok(QueryExpr::ConfigCommand(ConfigCommand::Put {
88                    collection,
89                    key,
90                    value,
91                    value_type,
92                    tags,
93                }))
94            }
95            "GET" => Ok(QueryExpr::ConfigCommand(ConfigCommand::Get {
96                collection,
97                key: key.ok_or_else(|| {
98                    ParseError::expected(vec!["config key"], self.peek(), self.position())
99                })?,
100            })),
101            "RESOLVE" => Ok(QueryExpr::ConfigCommand(ConfigCommand::Resolve {
102                collection,
103                key: key.ok_or_else(|| {
104                    ParseError::expected(vec!["config key"], self.peek(), self.position())
105                })?,
106            })),
107            "ROTATE" => {
108                let key = key.ok_or_else(|| {
109                    ParseError::expected(vec!["config key"], self.peek(), self.position())
110                })?;
111                self.expect(Token::Eq)?;
112                let value = self.parse_value()?;
113                let value_type = self.parse_config_value_type()?;
114                let tags = self.parse_optional_config_tags()?;
115                if self.consume_ident_ci("TTL")? || self.consume_ident_ci("EXPIRE")? {
116                    self.consume_config_tail()?;
117                    return Ok(QueryExpr::ConfigCommand(
118                        ConfigCommand::InvalidVolatileOperation {
119                            operation: "TTL/EXPIRE".to_string(),
120                            collection,
121                            key: Some(key),
122                        },
123                    ));
124                }
125                Ok(QueryExpr::ConfigCommand(ConfigCommand::Rotate {
126                    collection,
127                    key,
128                    value,
129                    value_type,
130                    tags,
131                }))
132            }
133            "DELETE" => Ok(QueryExpr::ConfigCommand(ConfigCommand::Delete {
134                collection,
135                key: key.ok_or_else(|| {
136                    ParseError::expected(vec!["config key"], self.peek(), self.position())
137                })?,
138            })),
139            "HISTORY" => Ok(QueryExpr::ConfigCommand(ConfigCommand::History {
140                collection,
141                key: key.ok_or_else(|| {
142                    ParseError::expected(vec!["config key"], self.peek(), self.position())
143                })?,
144            })),
145            "LIST" => {
146                if key.is_some() {
147                    return Err(ParseError::expected(
148                        vec!["PREFIX", "LIMIT", "OFFSET"],
149                        self.peek(),
150                        self.position(),
151                    ));
152                }
153                let (prefix, limit, offset) = self.parse_config_list_tail()?;
154                Ok(QueryExpr::ConfigCommand(ConfigCommand::List {
155                    collection,
156                    prefix,
157                    limit,
158                    offset,
159                }))
160            }
161            "WATCH" => {
162                let (key, prefix) = if self.consume_ident_ci("PREFIX")? {
163                    (self.expect_ident_or_keyword()?.to_ascii_lowercase(), true)
164                } else {
165                    (
166                        key.ok_or_else(|| {
167                            ParseError::expected(
168                                vec!["config key", "PREFIX"],
169                                self.peek(),
170                                self.position(),
171                            )
172                        })?,
173                        false,
174                    )
175                };
176                let from_lsn = if self.consume(&Token::From)? || self.consume_ident_ci("FROM")? {
177                    if !self.consume_ident_ci("LSN")? {
178                        return Err(ParseError::expected(
179                            vec!["LSN"],
180                            self.peek(),
181                            self.position(),
182                        ));
183                    }
184                    Some(self.parse_float()?.round() as u64)
185                } else {
186                    None
187                };
188                Ok(QueryExpr::ConfigCommand(ConfigCommand::Watch {
189                    collection,
190                    key,
191                    prefix,
192                    from_lsn,
193                }))
194            }
195            _ => Ok(QueryExpr::ConfigCommand(
196                ConfigCommand::InvalidVolatileOperation {
197                    operation,
198                    collection,
199                    key,
200                },
201            )),
202        }
203    }
204
205    fn consume_config_tail(&mut self) -> Result<(), ParseError> {
206        while !matches!(self.peek(), Token::Eof) {
207            self.advance()?;
208        }
209        Ok(())
210    }
211
212    pub(crate) fn parse_config_list_after_list(&mut self) -> Result<QueryExpr, ParseError> {
213        if !self.consume_ident_ci("CONFIG")? {
214            return Err(ParseError::expected(
215                vec!["CONFIG"],
216                self.peek(),
217                self.position(),
218            ));
219        }
220        let collection = self.parse_config_collection_name()?;
221        let (prefix, limit, offset) = self.parse_config_list_tail()?;
222        Ok(QueryExpr::ConfigCommand(ConfigCommand::List {
223            collection,
224            prefix,
225            limit,
226            offset,
227        }))
228    }
229
230    pub(crate) fn parse_config_watch_after_watch(&mut self) -> Result<QueryExpr, ParseError> {
231        if !self.consume_ident_ci("CONFIG")? {
232            return Err(ParseError::expected(
233                vec!["CONFIG"],
234                self.peek(),
235                self.position(),
236            ));
237        }
238        let collection = self.parse_config_collection_name()?;
239        let (key, prefix) = if self.consume_ident_ci("PREFIX")? {
240            (self.expect_ident_or_keyword()?.to_ascii_lowercase(), true)
241        } else {
242            (self.expect_ident_or_keyword()?.to_ascii_lowercase(), false)
243        };
244        let from_lsn = if self.consume(&Token::From)? || self.consume_ident_ci("FROM")? {
245            if !self.consume_ident_ci("LSN")? {
246                return Err(ParseError::expected(
247                    vec!["LSN"],
248                    self.peek(),
249                    self.position(),
250                ));
251            }
252            Some(self.parse_float()?.round() as u64)
253        } else {
254            None
255        };
256        Ok(QueryExpr::ConfigCommand(ConfigCommand::Watch {
257            collection,
258            key,
259            prefix,
260            from_lsn,
261        }))
262    }
263
264    fn parse_config_list_tail(
265        &mut self,
266    ) -> Result<(Option<String>, Option<usize>, usize), ParseError> {
267        let mut prefix = None;
268        let mut limit = None;
269        let mut offset = 0usize;
270        loop {
271            if self.consume_ident_ci("PREFIX")? {
272                prefix = Some(self.expect_ident_or_keyword()?.to_ascii_lowercase());
273            } else if self.consume(&Token::Limit)? || self.consume_ident_ci("LIMIT")? {
274                limit = Some(self.parse_float()?.round().max(0.0) as usize);
275            } else if self.consume(&Token::Offset)? || self.consume_ident_ci("OFFSET")? {
276                offset = self.parse_float()?.round().max(0.0) as usize;
277            } else {
278                break;
279            }
280        }
281        Ok((prefix, limit, offset))
282    }
283
284    fn parse_config_collection_name(&mut self) -> Result<String, ParseError> {
285        let mut collection = self.expect_ident_or_keyword()?.to_ascii_lowercase();
286        if self.consume(&Token::Dot)? {
287            let next = self.expect_ident_or_keyword()?.to_ascii_lowercase();
288            collection = format!("{collection}.{next}");
289        }
290        Ok(collection)
291    }
292
293    fn parse_optional_config_tags(&mut self) -> Result<Vec<String>, ParseError> {
294        if self.consume_ident_ci("TAGS")? {
295            self.parse_kv_tag_list()
296        } else {
297            Ok(Vec::new())
298        }
299    }
300
301    fn parse_config_value_type(&mut self) -> Result<Option<ConfigValueType>, ParseError> {
302        let has_with = self.consume(&Token::With)?;
303        let has_type = self.consume_ident_ci("TYPE")?;
304        let has_schema = if !has_type {
305            self.consume(&Token::Schema)?
306        } else {
307            false
308        };
309        if !has_with && !has_type && !has_schema {
310            return Ok(None);
311        }
312        if has_with && !has_type && !has_schema {
313            return Err(ParseError::expected(
314                vec!["TYPE", "SCHEMA"],
315                self.peek(),
316                self.position(),
317            ));
318        }
319        let raw_type = self.expect_ident_or_keyword()?;
320        let Some(value_type) = ConfigValueType::parse(&raw_type) else {
321            return Err(ParseError::expected(
322                vec!["bool", "int", "string", "url", "object", "array"],
323                self.peek(),
324                self.position(),
325            ));
326        };
327        Ok(Some(value_type))
328    }
329}