1use super::common::{field, parse_field_fallback};
2use crate::ast::{Field, Filter, FilterOperator, FilterValue, Quantifier};
3use crate::error::ParseError;
4
5type OperatorValueResult = (bool, String, Option<Quantifier>, Option<String>, String);
6
7pub fn parse_filter(field_str: &str, value_str: &str) -> Result<Filter, ParseError> {
56 let parsed_field = parse_field_string(field_str)?;
57 let (negated, operator_str, quantifier, language, value) = parse_operator_value(value_str)?;
58
59 let operator = parse_operator(&operator_str)?;
60 validate_operator_quantifier(&operator, &quantifier, &language)?;
61 let parsed_value = parse_value(&operator, &quantifier, &value)?;
62
63 Ok(Filter {
64 field: parsed_field,
65 operator,
66 value: parsed_value,
67 quantifier,
68 language,
69 negated,
70 })
71}
72
73pub fn parse_field_string(field_str: &str) -> Result<Field, ParseError> {
74 match field(field_str) {
75 Ok((_, field)) => Ok(field),
76 Err(_) => parse_field_fallback(field_str),
77 }
78}
79
80fn parse_operator_value(value_str: &str) -> Result<OperatorValueResult, ParseError> {
81 let parts: Vec<&str> = value_str.split('.').collect();
82
83 let (negated, rest) = if parts.first() == Some(&"not") {
84 (true, &parts[1..])
85 } else {
86 (false, parts.as_slice())
87 };
88
89 if rest.is_empty() {
90 return Err(ParseError::MissingOperatorOrValue);
91 }
92
93 let operator_part = rest[0];
94
95 let (operator, mut quantifier, mut language) =
96 if operator_part.contains('(') && operator_part.ends_with(')') {
97 let open_paren = operator_part.find('(').unwrap();
98 let op = &operator_part[..open_paren];
99 let quant_or_lang = &operator_part[open_paren + 1..operator_part.len() - 1];
100
101 match quant_or_lang {
102 "any" => (op.to_string(), Some(Quantifier::Any), None),
103 "all" => (op.to_string(), Some(Quantifier::All), None),
104 _ => (op.to_string(), None, Some(quant_or_lang.to_string())),
105 }
106 } else {
107 (operator_part.to_string(), None, None)
108 };
109
110 if rest.len() == 1 {
111 return Ok((negated, operator, quantifier, language, String::new()));
112 }
113
114 let (value_quant, value_lang, value) = extract_quantifier_or_language(&rest[1..])?;
115
116 if quantifier.is_none() {
117 quantifier = value_quant;
118 }
119 if language.is_none() {
120 language = value_lang;
121 }
122
123 Ok((negated, operator, quantifier, language, value))
124}
125
126fn extract_quantifier_or_language(
127 parts: &[&str],
128) -> Result<(Option<Quantifier>, Option<String>, String), ParseError> {
129 let (quantifier, language, value) = match parts {
130 [rest] => {
131 if rest.is_empty() {
132 (None, None, String::new())
133 } else {
134 (None, None, rest.to_string())
135 }
136 }
137 [quant_or_lang, value] => {
138 if quant_or_lang.ends_with(')') {
139 if quant_or_lang.starts_with('(') {
140 let inner = &quant_or_lang[1..quant_or_lang.len() - 1];
141 if inner == "any" {
142 (Some(Quantifier::Any), None, value.to_string())
143 } else if inner == "all" {
144 (Some(Quantifier::All), None, value.to_string())
145 } else {
146 (None, Some(inner.to_string()), value.to_string())
147 }
148 } else {
149 (None, None, format!("{}.{}", quant_or_lang, value))
150 }
151 } else {
152 (None, None, format!("{}.{}", quant_or_lang, value))
153 }
154 }
155 [quantifier, language, value] => {
156 let quant = if quantifier.ends_with(')') {
157 match *quantifier {
158 "(any)" => Some(Quantifier::Any),
159 "(all)" => Some(Quantifier::All),
160 _ => return Err(ParseError::InvalidQuantifier(quantifier.to_string())),
161 }
162 } else {
163 return Err(ParseError::InvalidQuantifier(quantifier.to_string()));
164 };
165
166 if language.ends_with(')') {
167 let inner = &language[1..language.len() - 1];
168 (quant, Some(inner.to_string()), value.to_string())
169 } else {
170 (quant, None, format!("{}.{}", language, value))
171 }
172 }
173 _ => return Err(ParseError::InvalidFilterFormat(format!("{:?}", parts))),
174 };
175
176 Ok((quantifier, language, value))
177}
178
179fn parse_operator(op_str: &str) -> Result<FilterOperator, ParseError> {
180 match op_str.to_lowercase().as_str() {
181 "eq" => Ok(FilterOperator::Eq),
182 "neq" => Ok(FilterOperator::Neq),
183 "gt" => Ok(FilterOperator::Gt),
184 "gte" => Ok(FilterOperator::Gte),
185 "lt" => Ok(FilterOperator::Lt),
186 "lte" => Ok(FilterOperator::Lte),
187 "like" => Ok(FilterOperator::Like),
188 "ilike" => Ok(FilterOperator::Ilike),
189 "match" => Ok(FilterOperator::Match),
190 "imatch" => Ok(FilterOperator::Imatch),
191 "in" => Ok(FilterOperator::In),
192 "is" => Ok(FilterOperator::Is),
193 "fts" => Ok(FilterOperator::Fts),
194 "plfts" => Ok(FilterOperator::Plfts),
195 "phfts" => Ok(FilterOperator::Phfts),
196 "wfts" => Ok(FilterOperator::Wfts),
197 "cs" => Ok(FilterOperator::Cs),
198 "cd" => Ok(FilterOperator::Cd),
199 "ov" => Ok(FilterOperator::Ov),
200 "sl" => Ok(FilterOperator::Sl),
201 "sr" => Ok(FilterOperator::Sr),
202 "nxl" => Ok(FilterOperator::Nxl),
203 "nxr" => Ok(FilterOperator::Nxr),
204 "adj" => Ok(FilterOperator::Adj),
205 _ => Err(ParseError::UnknownOperator(op_str.to_string())),
206 }
207}
208
209fn validate_operator_quantifier(
210 operator: &FilterOperator,
211 quantifier: &Option<Quantifier>,
212 language: &Option<String>,
213) -> Result<(), ParseError> {
214 match (operator, quantifier, language) {
215 (FilterOperator::In, Some(_), _)
216 | (FilterOperator::Cs, Some(_), _)
217 | (FilterOperator::Cd, Some(_), _)
218 | (FilterOperator::Ov, Some(_), _)
219 | (FilterOperator::Sl, Some(_), _)
220 | (FilterOperator::Sr, Some(_), _)
221 | (FilterOperator::Nxl, Some(_), _)
222 | (FilterOperator::Nxr, Some(_), _)
223 | (FilterOperator::Adj, Some(_), _)
224 | (FilterOperator::Is, Some(_), _) => Err(ParseError::QuantifierNotSupported),
225 (
226 FilterOperator::Fts
227 | FilterOperator::Plfts
228 | FilterOperator::Phfts
229 | FilterOperator::Wfts,
230 Some(Quantifier::Any | Quantifier::All),
231 _,
232 ) => Err(ParseError::InvalidFtsLanguage(
233 "any/all not supported for FTS".to_string(),
234 )),
235 _ => Ok(()),
236 }
237}
238
239fn parse_value(
240 operator: &FilterOperator,
241 quantifier: &Option<Quantifier>,
242 value_str: &str,
243) -> Result<FilterValue, ParseError> {
244 match (operator, quantifier) {
245 (FilterOperator::In, _) => parse_list_value(value_str, '(', ')'),
246 (FilterOperator::Cs | FilterOperator::Cd, _) => {
247 Ok(FilterValue::Single(value_str.to_string()))
248 }
249 (FilterOperator::Ov, _) => parse_list_value(value_str, '(', ')'),
250 (
251 FilterOperator::Eq
252 | FilterOperator::Neq
253 | FilterOperator::Gt
254 | FilterOperator::Gte
255 | FilterOperator::Lt
256 | FilterOperator::Lte
257 | FilterOperator::Like
258 | FilterOperator::Ilike
259 | FilterOperator::Match
260 | FilterOperator::Imatch,
261 Some(Quantifier::Any | Quantifier::All),
262 ) => parse_list_value(value_str, '{', '}'),
263 _ => Ok(FilterValue::Single(value_str.to_string())),
264 }
265}
266
267fn parse_list_value(value_str: &str, open: char, close: char) -> Result<FilterValue, ParseError> {
268 if value_str.starts_with(open) && value_str.ends_with(close) {
269 let inner = &value_str[1..value_str.len() - 1];
270 let items: Vec<String> = inner.split(',').map(|s| s.trim().to_string()).collect();
271 Ok(FilterValue::List(items))
272 } else {
273 Err(ParseError::ExpectedListFormat(format!(
274 "expected list with {} and {}",
275 open, close
276 )))
277 }
278}
279
280pub fn reserved_key(key: &str) -> bool {
281 matches!(
282 key,
283 "select" | "order" | "limit" | "offset" | "on_conflict" | "columns" | "returning"
284 )
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_parse_filter_eq() {
293 let result = parse_filter("id", "eq.1");
294 assert!(result.is_ok());
295 let filter = result.unwrap();
296 assert_eq!(filter.operator, FilterOperator::Eq);
297 assert_eq!(filter.value, FilterValue::Single("1".to_string()));
298 assert!(!filter.negated);
299 }
300
301 #[test]
302 fn test_parse_filter_negated() {
303 let result = parse_filter("status", "not.eq.active");
304 assert!(result.is_ok());
305 let filter = result.unwrap();
306 assert!(filter.negated);
307 }
308
309 #[test]
310 fn test_parse_filter_in_operator() {
311 let result = parse_filter("status", "in.(active,pending)");
312 assert!(result.is_ok());
313 let filter = result.unwrap();
314 assert_eq!(filter.operator, FilterOperator::In);
315 match filter.value {
316 FilterValue::List(items) => {
317 assert_eq!(items.len(), 2);
318 assert!(items.contains(&"active".to_string()));
319 }
320 _ => panic!("Expected list value"),
321 }
322 }
323
324 #[test]
325 fn test_parse_filter_with_quantifier() {
326 let result = parse_filter("status", "eq(any).{active,pending}");
327 assert!(result.is_ok());
328 let filter = result.unwrap();
329 assert_eq!(filter.quantifier, Some(Quantifier::Any));
330 assert!(matches!(filter.value, FilterValue::List(_)));
331 }
332
333 #[test]
334 fn test_parse_filter_with_json_path() {
335 let result = parse_filter("data->name", "eq.test");
336 assert!(result.is_ok());
337 let filter = result.unwrap();
338 assert_eq!(filter.field.name, "data");
339 assert_eq!(filter.field.json_path.len(), 1);
340 }
341
342 #[test]
343 fn test_parse_filter_with_type_cast() {
344 let result = parse_filter("price", "eq.100");
345 assert!(result.is_ok());
346 assert!(result.is_ok());
347 }
348
349 #[test]
350 fn test_parse_filter_fts_with_language() {
351 let result = parse_filter("content", "fts(english).search");
352 assert!(result.is_ok());
353 let filter = result.unwrap();
354 assert_eq!(filter.language, Some("english".to_string()));
355 }
356
357 #[test]
358 fn test_parse_filter_unknown_operator() {
359 let result = parse_filter("id", "invalid.1");
360 assert!(matches!(result, Err(ParseError::UnknownOperator(_))));
361 }
362
363 #[test]
364 fn test_parse_filter_invalid_quantifier() {
365 let result = parse_filter("status", "is(any).null");
366 assert!(matches!(result, Err(ParseError::QuantifierNotSupported)));
367 }
368
369 #[test]
370 fn test_parse_filter_unclosed_parenthesis() {
371 let result = parse_filter("status", "in.(active");
372 assert!(matches!(result, Err(ParseError::ExpectedListFormat(_))));
373 }
374
375 #[test]
376 fn test_reserved_key() {
377 assert!(reserved_key("select"));
378 assert!(reserved_key("order"));
379 assert!(reserved_key("limit"));
380 assert!(!reserved_key("id"));
381 }
382
383 #[test]
384 fn test_parse_filter_with_whitespace_in_list() {
385 let result = parse_filter("status", "in.(active, pending, closed)");
386 assert!(result.is_ok());
387 let filter = result.unwrap();
388 assert_eq!(filter.operator, FilterOperator::In);
389 match filter.value {
390 FilterValue::List(items) => {
391 assert_eq!(items.len(), 3);
392 assert!(items.contains(&"active".to_string()));
393 assert!(items.contains(&"pending".to_string()));
394 assert!(items.contains(&"closed".to_string()));
395 }
396 _ => panic!("Expected list value"),
397 }
398 }
399
400 #[test]
401 fn test_parse_filter_comparison_operators() {
402 let result = parse_filter("age", "gt.18");
404 assert!(result.is_ok());
405 let filter = result.unwrap();
406 assert_eq!(filter.operator, FilterOperator::Gt);
407 assert_eq!(filter.value, FilterValue::Single("18".to_string()));
408
409 let result = parse_filter("age", "gte.21");
411 assert!(result.is_ok());
412 assert_eq!(result.unwrap().operator, FilterOperator::Gte);
413
414 let result = parse_filter("age", "lt.65");
416 assert!(result.is_ok());
417 assert_eq!(result.unwrap().operator, FilterOperator::Lt);
418
419 let result = parse_filter("age", "lte.65");
421 assert!(result.is_ok());
422 assert_eq!(result.unwrap().operator, FilterOperator::Lte);
423
424 let result = parse_filter("status", "neq.inactive");
426 assert!(result.is_ok());
427 assert_eq!(result.unwrap().operator, FilterOperator::Neq);
428 }
429
430 #[test]
431 fn test_parse_filter_match_operators() {
432 let result = parse_filter("name", "match.^John");
434 assert!(result.is_ok());
435 let filter = result.unwrap();
436 assert_eq!(filter.operator, FilterOperator::Match);
437
438 let result = parse_filter("name", "imatch.^john");
440 assert!(result.is_ok());
441 assert_eq!(result.unwrap().operator, FilterOperator::Imatch);
442 }
443
444 #[test]
445 fn test_parse_filter_array_operators() {
446 let result = parse_filter("tags", "cs.{rust}");
448 assert!(result.is_ok());
449 assert_eq!(result.unwrap().operator, FilterOperator::Cs);
450
451 let result = parse_filter("tags", "cd.{rust,elixir}");
453 assert!(result.is_ok());
454 assert_eq!(result.unwrap().operator, FilterOperator::Cd);
455
456 let result = parse_filter("tags", "ov.(rust,elixir)");
458 assert!(result.is_ok());
459 assert_eq!(result.unwrap().operator, FilterOperator::Ov);
460 }
461
462 #[test]
463 fn test_parse_filter_range_operators() {
464 let result = parse_filter("range", "sl.[1,10)");
466 assert!(result.is_ok());
467 assert_eq!(result.unwrap().operator, FilterOperator::Sl);
468
469 let result = parse_filter("range", "sr.[1,10)");
471 assert!(result.is_ok());
472 assert_eq!(result.unwrap().operator, FilterOperator::Sr);
473
474 let result = parse_filter("range", "nxl.[1,10)");
476 assert!(result.is_ok());
477 assert_eq!(result.unwrap().operator, FilterOperator::Nxl);
478
479 let result = parse_filter("range", "nxr.[1,10)");
481 assert!(result.is_ok());
482 assert_eq!(result.unwrap().operator, FilterOperator::Nxr);
483
484 let result = parse_filter("range", "adj.[1,10)");
486 assert!(result.is_ok());
487 assert_eq!(result.unwrap().operator, FilterOperator::Adj);
488 }
489
490 #[test]
491 fn test_parse_filter_fts_operators() {
492 let result = parse_filter("content", "plfts.search");
494 assert!(result.is_ok());
495 assert_eq!(result.unwrap().operator, FilterOperator::Plfts);
496
497 let result = parse_filter("content", "phfts.search phrase");
499 assert!(result.is_ok());
500 assert_eq!(result.unwrap().operator, FilterOperator::Phfts);
501
502 let result = parse_filter("content", "wfts.search query");
504 assert!(result.is_ok());
505 assert_eq!(result.unwrap().operator, FilterOperator::Wfts);
506 }
507
508 #[test]
509 fn test_parse_filter_negated_operators() {
510 let result = parse_filter("age", "not.gt.18");
512 assert!(result.is_ok());
513 let filter = result.unwrap();
514 assert!(filter.negated);
515 assert_eq!(filter.operator, FilterOperator::Gt);
516
517 let result = parse_filter("status", "not.in.(active,pending)");
519 assert!(result.is_ok());
520 let filter = result.unwrap();
521 assert!(filter.negated);
522 assert_eq!(filter.operator, FilterOperator::In);
523
524 let result = parse_filter("name", "not.like.*John*");
526 assert!(result.is_ok());
527 let filter = result.unwrap();
528 assert!(filter.negated);
529 assert_eq!(filter.operator, FilterOperator::Like);
530 }
531}