reddb_server/storage/query/parser/
config.rs1use 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}