1use super::types::*;
7use crate::error::{Error, Result};
8use nom::{
9 branch::alt,
10 bytes::complete::{tag, take_until, take_while1},
11 character::complete::{char, digit1},
12 combinator::{map, opt, value},
13 multi::{many0, separated_list0},
14 sequence::preceded,
15 IResult,
16};
17use percent_encoding::percent_decode_str;
18
19pub fn parse_query_params(query: &str) -> Result<QueryParams> {
21 let mut params = QueryParams::default();
22
23 if query.is_empty() {
24 return Ok(params);
25 }
26
27 let mut pairs: Vec<(&str, &str)> = query
29 .split('&')
30 .filter_map(|pair| {
31 let mut parts = pair.splitn(2, '=');
32 Some((parts.next()?, parts.next().unwrap_or("")))
33 })
34 .collect();
35 pairs.sort_by_key(|(k, _)| *k);
36 params.canonical = pairs
37 .iter()
38 .map(|(k, v)| format!("{}={}", k, v))
39 .collect::<Vec<_>>()
40 .join("&");
41
42 for (key, value) in pairs {
43 let decoded_value = percent_decode_str(value)
44 .decode_utf8()
45 .map_err(|_| Error::InvalidQueryParam(key.into()))?
46 .to_string();
47
48 match key {
49 "select" => {
50 params.select = parse_select(&decoded_value)?;
51 }
52 "order" => {
53 let (path, terms) = parse_order_param(&decoded_value)?;
54 params.order.push((path, terms));
55 }
56 "limit" => {
57 let limit: i64 = decoded_value
58 .parse()
59 .map_err(|_| Error::InvalidQueryParam("limit".into()))?;
60 params.ranges.entry(String::new()).or_default().limit = Some(limit);
61 }
62 "offset" => {
63 let offset: i64 = decoded_value
64 .parse()
65 .map_err(|_| Error::InvalidQueryParam("offset".into()))?;
66 params.ranges.entry(String::new()).or_default().offset = offset;
67 }
68 "columns" => {
69 params.columns = Some(
70 decoded_value
71 .split(',')
72 .map(|s| s.trim().to_string())
73 .collect(),
74 );
75 }
76 "on_conflict" => {
77 params.on_conflict = Some(
78 decoded_value
79 .split(',')
80 .map(|s| s.trim().to_string())
81 .collect(),
82 );
83 }
84 "and" | "or" => {
85 let logic = parse_logic_param(key, &decoded_value)?;
86 params.logic.push((vec![], logic));
87 }
88 key if !key.starts_with('_') => {
89 let (path, filter) = parse_filter_param(key, &decoded_value)?;
91 if path.is_empty() {
92 params.filter_fields.insert(filter.field.name.clone());
93 params.filters_root.push(filter);
94 } else {
95 params.filters.push((path, filter));
96 }
97 }
98 _ => {
99 params.params.push((key.to_string(), decoded_value));
101 }
102 }
103 }
104
105 Ok(params)
106}
107
108pub fn parse_select(input: &str) -> Result<Vec<SelectItem>> {
114 if input.is_empty() {
115 return Ok(vec![]);
116 }
117
118 match parse_select_items(input) {
119 Ok((_, items)) => Ok(items),
120 Err(_) => Err(Error::InvalidQueryParam("select".into())),
121 }
122}
123
124fn parse_select_items(input: &str) -> IResult<&str, Vec<SelectItem>> {
125 separated_list0(char(','), parse_select_item)(input)
126}
127
128fn parse_select_item(input: &str) -> IResult<&str, SelectItem> {
129 alt((
130 parse_spread_relation,
131 parse_relation_select,
132 parse_field_select,
133 ))(input)
134}
135
136fn parse_spread_relation(input: &str) -> IResult<&str, SelectItem> {
138 let (input, _) = tag("...")(input)?;
139 let (input, relation) = parse_identifier(input)?;
140 let (input, hint) = opt(preceded(char('!'), parse_identifier))(input)?;
141 let (input, join_type) = opt(preceded(char('!'), parse_join_type))(input)?;
142
143 Ok((
144 input,
145 SelectItem::SpreadRelation {
146 relation: relation.to_string(),
147 hint: hint.map(|s| s.to_string()),
148 join_type,
149 },
150 ))
151}
152
153fn parse_relation_select(input: &str) -> IResult<&str, SelectItem> {
155 let (input, name) = parse_identifier(input)?;
156 let (input, alias) = opt(preceded(char(':'), parse_identifier))(input)?;
157 let (input, hint) = opt(preceded(char('!'), parse_identifier))(input)?;
158 let (input, join_type) = opt(preceded(char('!'), parse_join_type))(input)?;
159 let (input, _) = char('(')(input)?;
160 let (input, _nested) = take_until(")")(input)?;
161 let (input, _) = char(')')(input)?;
162
163 Ok((
164 input,
165 SelectItem::Relation {
166 relation: name.to_string(),
167 alias: alias.map(|s| s.to_string()),
168 hint: hint.map(|s| s.to_string()),
169 join_type,
170 },
171 ))
172}
173
174fn parse_field_select(input: &str) -> IResult<&str, SelectItem> {
176 let (input, aggregate) = opt(parse_aggregate_prefix)(input)?;
178
179 let (input, name) = parse_identifier(input)?;
180 let (input, json_path) = parse_json_path(input)?;
181
182 let (input, aggregate_cast) = if aggregate.is_some() {
184 let (input, _) = char(')')(input)?;
185 let (input, cast) = opt(preceded(tag("::"), parse_identifier))(input)?;
186 (input, cast.map(|s| s.to_string()))
187 } else {
188 (input, None)
189 };
190
191 let (input, cast) = if aggregate.is_none() {
192 opt(preceded(tag("::"), parse_identifier))(input)?
193 } else {
194 (input, None)
195 };
196
197 let (input, alias) = opt(preceded(char(':'), parse_identifier))(input)?;
198
199 Ok((
200 input,
201 SelectItem::Field {
202 field: Field {
203 name: name.to_string(),
204 json_path,
205 },
206 aggregate,
207 aggregate_cast,
208 cast: cast.map(|s| s.to_string()),
209 alias: alias.map(|s| s.to_string()),
210 },
211 ))
212}
213
214fn parse_aggregate_prefix(input: &str) -> IResult<&str, AggregateFunction> {
215 alt((
216 value(AggregateFunction::Sum, tag("sum(")),
217 value(AggregateFunction::Avg, tag("avg(")),
218 value(AggregateFunction::Max, tag("max(")),
219 value(AggregateFunction::Min, tag("min(")),
220 value(AggregateFunction::Count, tag("count(")),
221 ))(input)
222}
223
224fn parse_join_type(input: &str) -> IResult<&str, JoinType> {
225 alt((
226 value(JoinType::Inner, tag("inner")),
227 value(JoinType::Left, tag("left")),
228 ))(input)
229}
230
231fn parse_filter_param(key: &str, value: &str) -> Result<(EmbedPath, Filter)> {
237 let (path, field_name) = parse_filter_key(key)?;
239
240 let op_expr = parse_filter_value(value)?;
242
243 let filter = Filter::new(Field::simple(field_name), op_expr);
244 Ok((path, filter))
245}
246
247fn parse_filter_key(key: &str) -> Result<(EmbedPath, String)> {
249 let parts: Vec<&str> = key.split('.').collect();
250 if parts.is_empty() {
251 return Err(Error::InvalidQueryParam(key.into()));
252 }
253
254 if parts.len() == 1 {
255 return Ok((vec![], parts[0].to_string()));
256 }
257
258 let path: Vec<String> = parts[..parts.len() - 1]
259 .iter()
260 .map(|s| s.to_string())
261 .collect();
262 let field = parts.last().unwrap().to_string();
263 Ok((path, field))
264}
265
266fn parse_filter_value(value: &str) -> Result<OpExpr> {
268 let (value, negated) = if let Some(rest) = value.strip_prefix("not.") {
269 (rest, true)
270 } else {
271 (value, false)
272 };
273
274 let operation = parse_operation(value)?;
275 Ok(OpExpr { negated, operation })
276}
277
278fn parse_operation(value: &str) -> Result<Operation> {
280 if let Some(rest) = value.strip_prefix("eq.") {
282 return Ok(Operation::Quant {
283 op: QuantOperator::Equal,
284 quantifier: None,
285 value: rest.to_string(),
286 });
287 }
288 if let Some(rest) = value.strip_prefix("neq.") {
289 return Ok(Operation::Simple {
290 op: SimpleOperator::NotEqual,
291 value: rest.to_string(),
292 });
293 }
294 if let Some(rest) = value.strip_prefix("gt.") {
295 return Ok(Operation::Quant {
296 op: QuantOperator::GreaterThan,
297 quantifier: None,
298 value: rest.to_string(),
299 });
300 }
301 if let Some(rest) = value.strip_prefix("gte.") {
302 return Ok(Operation::Quant {
303 op: QuantOperator::GreaterThanEqual,
304 quantifier: None,
305 value: rest.to_string(),
306 });
307 }
308 if let Some(rest) = value.strip_prefix("lt.") {
309 return Ok(Operation::Quant {
310 op: QuantOperator::LessThan,
311 quantifier: None,
312 value: rest.to_string(),
313 });
314 }
315 if let Some(rest) = value.strip_prefix("lte.") {
316 return Ok(Operation::Quant {
317 op: QuantOperator::LessThanEqual,
318 quantifier: None,
319 value: rest.to_string(),
320 });
321 }
322 if let Some(rest) = value.strip_prefix("like.") {
323 return Ok(Operation::Quant {
324 op: QuantOperator::Like,
325 quantifier: None,
326 value: rest.to_string(),
327 });
328 }
329 if let Some(rest) = value.strip_prefix("ilike.") {
330 return Ok(Operation::Quant {
331 op: QuantOperator::ILike,
332 quantifier: None,
333 value: rest.to_string(),
334 });
335 }
336 if let Some(rest) = value.strip_prefix("match.") {
337 return Ok(Operation::Quant {
338 op: QuantOperator::Match,
339 quantifier: None,
340 value: rest.to_string(),
341 });
342 }
343 if let Some(rest) = value.strip_prefix("imatch.") {
344 return Ok(Operation::Quant {
345 op: QuantOperator::IMatch,
346 quantifier: None,
347 value: rest.to_string(),
348 });
349 }
350
351 if let Some(rest) = value.strip_prefix("cs.") {
353 return Ok(Operation::Simple {
354 op: SimpleOperator::Contains,
355 value: rest.to_string(),
356 });
357 }
358 if let Some(rest) = value.strip_prefix("cd.") {
359 return Ok(Operation::Simple {
360 op: SimpleOperator::Contained,
361 value: rest.to_string(),
362 });
363 }
364 if let Some(rest) = value.strip_prefix("ov.") {
365 return Ok(Operation::Simple {
366 op: SimpleOperator::Overlap,
367 value: rest.to_string(),
368 });
369 }
370 if let Some(rest) = value.strip_prefix("sl.") {
371 return Ok(Operation::Simple {
372 op: SimpleOperator::StrictlyLeft,
373 value: rest.to_string(),
374 });
375 }
376 if let Some(rest) = value.strip_prefix("sr.") {
377 return Ok(Operation::Simple {
378 op: SimpleOperator::StrictlyRight,
379 value: rest.to_string(),
380 });
381 }
382 if let Some(rest) = value.strip_prefix("nxr.") {
383 return Ok(Operation::Simple {
384 op: SimpleOperator::NotExtendsRight,
385 value: rest.to_string(),
386 });
387 }
388 if let Some(rest) = value.strip_prefix("nxl.") {
389 return Ok(Operation::Simple {
390 op: SimpleOperator::NotExtendsLeft,
391 value: rest.to_string(),
392 });
393 }
394 if let Some(rest) = value.strip_prefix("adj.") {
395 return Ok(Operation::Simple {
396 op: SimpleOperator::Adjacent,
397 value: rest.to_string(),
398 });
399 }
400
401 if let Some(rest) = value.strip_prefix("in.") {
403 let values = parse_in_list(rest)?;
404 return Ok(Operation::In(values));
405 }
406
407 if let Some(rest) = value.strip_prefix("is.") {
409 let is_val = match rest {
410 "null" => IsValue::Null,
411 "true" => IsValue::True,
412 "false" => IsValue::False,
413 "unknown" => IsValue::Unknown,
414 _ => return Err(Error::InvalidQueryParam(format!("is.{}", rest))),
415 };
416 return Ok(Operation::Is(is_val));
417 }
418
419 if let Some(rest) = value.strip_prefix("isdistinct.") {
421 return Ok(Operation::IsDistinctFrom(rest.to_string()));
422 }
423
424 if let Some(rest) = value.strip_prefix("fts") {
426 return parse_fts(FtsOperator::Fts, rest);
427 }
428 if let Some(rest) = value.strip_prefix("plfts") {
429 return parse_fts(FtsOperator::Plain, rest);
430 }
431 if let Some(rest) = value.strip_prefix("phfts") {
432 return parse_fts(FtsOperator::Phrase, rest);
433 }
434 if let Some(rest) = value.strip_prefix("wfts") {
435 return parse_fts(FtsOperator::Websearch, rest);
436 }
437
438 Err(Error::InvalidQueryParam(value.into()))
439}
440
441fn parse_in_list(value: &str) -> Result<Vec<String>> {
443 let value = value
444 .strip_prefix('(')
445 .and_then(|s| s.strip_suffix(')'))
446 .ok_or_else(|| Error::InvalidQueryParam(format!("in.{}", value)))?;
447
448 Ok(value.split(',').map(|s| s.trim().to_string()).collect())
449}
450
451fn parse_fts(op: FtsOperator, rest: &str) -> Result<Operation> {
453 if let Some(rest) = rest.strip_prefix('(') {
454 let (lang, query) = rest
456 .split_once(").")
457 .ok_or_else(|| Error::InvalidQueryParam(format!("fts{}", rest)))?;
458 return Ok(Operation::Fts {
459 op,
460 language: Some(lang.to_string()),
461 value: query.to_string(),
462 });
463 }
464
465 let query = rest
466 .strip_prefix('.')
467 .ok_or_else(|| Error::InvalidQueryParam(format!("fts{}", rest)))?;
468 Ok(Operation::Fts {
469 op,
470 language: None,
471 value: query.to_string(),
472 })
473}
474
475fn parse_order_param(value: &str) -> Result<(EmbedPath, Vec<OrderTerm>)> {
481 let terms: Vec<OrderTerm> = value
482 .split(',')
483 .map(|s| parse_order_term(s.trim()))
484 .collect::<Result<Vec<_>>>()?;
485 Ok((vec![], terms))
486}
487
488fn parse_order_term(value: &str) -> Result<OrderTerm> {
489 let parts: Vec<&str> = value.split('.').collect();
490 if parts.is_empty() {
491 return Err(Error::InvalidQueryParam("order".into()));
492 }
493
494 let field_name = parts[0];
495 let mut direction = None;
496 let mut nulls = None;
497
498 for part in &parts[1..] {
499 match *part {
500 "asc" => direction = Some(OrderDirection::Asc),
501 "desc" => direction = Some(OrderDirection::Desc),
502 "nullsfirst" => nulls = Some(OrderNulls::First),
503 "nullslast" => nulls = Some(OrderNulls::Last),
504 _ => {}
505 }
506 }
507
508 Ok(OrderTerm::Field {
509 field: Field::simple(field_name),
510 direction,
511 nulls,
512 })
513}
514
515fn parse_logic_param(op: &str, value: &str) -> Result<LogicTree> {
521 let logic_op = match op {
522 "and" => LogicOperator::And,
523 "or" => LogicOperator::Or,
524 _ => return Err(Error::InvalidQueryParam(op.into())),
525 };
526
527 let value = value
529 .strip_prefix('(')
530 .and_then(|s| s.strip_suffix(')'))
531 .ok_or_else(|| Error::InvalidQueryParam(format!("{}={}", op, value)))?;
532
533 let children: Vec<LogicTree> = value
534 .split(',')
535 .map(|s| {
536 let (key, val) = s
537 .split_once('.')
538 .ok_or_else(|| Error::InvalidQueryParam(s.into()))?;
539 let (_, filter) = parse_filter_param(key, val)?;
540 Ok(LogicTree::Stmt(filter))
541 })
542 .collect::<Result<Vec<_>>>()?;
543
544 Ok(LogicTree::Expr {
545 negated: false,
546 op: logic_op,
547 children,
548 })
549}
550
551fn parse_identifier(input: &str) -> IResult<&str, &str> {
556 take_while1(|c: char| c.is_alphanumeric() || c == '_')(input)
557}
558
559fn parse_json_path(input: &str) -> IResult<&str, JsonPath> {
560 many0(alt((parse_arrow, parse_double_arrow)))(input)
561}
562
563fn parse_arrow(input: &str) -> IResult<&str, JsonOperation> {
564 let (input, _) = tag("->")(input)?;
565 let (input, operand) = alt((
566 map(digit1, |s: &str| JsonOperand::Idx(s.parse().unwrap_or(0))),
567 map(parse_identifier, |s| JsonOperand::Key(s.to_string())),
568 ))(input)?;
569 Ok((input, JsonOperation::Arrow(operand)))
570}
571
572fn parse_double_arrow(input: &str) -> IResult<&str, JsonOperation> {
573 let (input, _) = tag("->>")(input)?;
574 let (input, operand) = alt((
575 map(digit1, |s: &str| JsonOperand::Idx(s.parse().unwrap_or(0))),
576 map(parse_identifier, |s| JsonOperand::Key(s.to_string())),
577 ))(input)?;
578 Ok((input, JsonOperation::DoubleArrow(operand)))
579}
580
581#[cfg(test)]
582mod tests {
583 use super::*;
584
585 #[test]
586 fn test_parse_simple_filter() {
587 let params = parse_query_params("name=eq.John").unwrap();
588 assert_eq!(params.filters_root.len(), 1);
589 assert_eq!(params.filters_root[0].field.name, "name");
590 }
591
592 #[test]
593 fn test_parse_negated_filter() {
594 let params = parse_query_params("status=not.eq.active").unwrap();
595 assert!(params.filters_root[0].op_expr.negated);
596 }
597
598 #[test]
599 fn test_parse_in_filter() {
600 let params = parse_query_params("id=in.(1,2,3)").unwrap();
601 match ¶ms.filters_root[0].op_expr.operation {
602 Operation::In(values) => {
603 assert_eq!(values, &vec!["1", "2", "3"]);
604 }
605 _ => panic!("Expected In operation"),
606 }
607 }
608
609 #[test]
610 fn test_parse_is_null() {
611 let params = parse_query_params("deleted_at=is.null").unwrap();
612 match ¶ms.filters_root[0].op_expr.operation {
613 Operation::Is(IsValue::Null) => {}
614 _ => panic!("Expected Is Null"),
615 }
616 }
617
618 #[test]
619 fn test_parse_order() {
620 let params = parse_query_params("order=name.asc,age.desc.nullslast").unwrap();
621 assert_eq!(params.order.len(), 1);
622 let (_, terms) = ¶ms.order[0];
623 assert_eq!(terms.len(), 2);
624 }
625
626 #[test]
627 fn test_parse_limit_offset() {
628 let params = parse_query_params("limit=10&offset=20").unwrap();
629 let range = params.ranges.get("").unwrap();
630 assert_eq!(range.limit, Some(10));
631 assert_eq!(range.offset, 20);
632 }
633
634 #[test]
635 fn test_parse_select() {
636 let items = parse_select("id,name,orders(id,amount)").unwrap();
637 assert_eq!(items.len(), 3);
638 }
639
640 #[test]
641 fn test_parse_fts() {
642 let params = parse_query_params("content=fts(english).search+term").unwrap();
643 match ¶ms.filters_root[0].op_expr.operation {
644 Operation::Fts {
645 op,
646 language,
647 value,
648 } => {
649 assert_eq!(*op, FtsOperator::Fts);
650 assert_eq!(language.as_deref(), Some("english"));
651 assert_eq!(value, "search+term");
652 }
653 _ => panic!("Expected FTS operation"),
654 }
655 }
656}