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].iter().map(|s| s.to_string()).collect();
259 let field = parts.last().unwrap().to_string();
260 Ok((path, field))
261}
262
263fn parse_filter_value(value: &str) -> Result<OpExpr> {
265 let (value, negated) = if let Some(rest) = value.strip_prefix("not.") {
266 (rest, true)
267 } else {
268 (value, false)
269 };
270
271 let operation = parse_operation(value)?;
272 Ok(OpExpr { negated, operation })
273}
274
275fn parse_operation(value: &str) -> Result<Operation> {
277 if let Some(rest) = value.strip_prefix("eq.") {
279 return Ok(Operation::Quant {
280 op: QuantOperator::Equal,
281 quantifier: None,
282 value: rest.to_string(),
283 });
284 }
285 if let Some(rest) = value.strip_prefix("neq.") {
286 return Ok(Operation::Simple {
287 op: SimpleOperator::NotEqual,
288 value: rest.to_string(),
289 });
290 }
291 if let Some(rest) = value.strip_prefix("gt.") {
292 return Ok(Operation::Quant {
293 op: QuantOperator::GreaterThan,
294 quantifier: None,
295 value: rest.to_string(),
296 });
297 }
298 if let Some(rest) = value.strip_prefix("gte.") {
299 return Ok(Operation::Quant {
300 op: QuantOperator::GreaterThanEqual,
301 quantifier: None,
302 value: rest.to_string(),
303 });
304 }
305 if let Some(rest) = value.strip_prefix("lt.") {
306 return Ok(Operation::Quant {
307 op: QuantOperator::LessThan,
308 quantifier: None,
309 value: rest.to_string(),
310 });
311 }
312 if let Some(rest) = value.strip_prefix("lte.") {
313 return Ok(Operation::Quant {
314 op: QuantOperator::LessThanEqual,
315 quantifier: None,
316 value: rest.to_string(),
317 });
318 }
319 if let Some(rest) = value.strip_prefix("like.") {
320 return Ok(Operation::Quant {
321 op: QuantOperator::Like,
322 quantifier: None,
323 value: rest.to_string(),
324 });
325 }
326 if let Some(rest) = value.strip_prefix("ilike.") {
327 return Ok(Operation::Quant {
328 op: QuantOperator::ILike,
329 quantifier: None,
330 value: rest.to_string(),
331 });
332 }
333 if let Some(rest) = value.strip_prefix("match.") {
334 return Ok(Operation::Quant {
335 op: QuantOperator::Match,
336 quantifier: None,
337 value: rest.to_string(),
338 });
339 }
340 if let Some(rest) = value.strip_prefix("imatch.") {
341 return Ok(Operation::Quant {
342 op: QuantOperator::IMatch,
343 quantifier: None,
344 value: rest.to_string(),
345 });
346 }
347
348 if let Some(rest) = value.strip_prefix("cs.") {
350 return Ok(Operation::Simple {
351 op: SimpleOperator::Contains,
352 value: rest.to_string(),
353 });
354 }
355 if let Some(rest) = value.strip_prefix("cd.") {
356 return Ok(Operation::Simple {
357 op: SimpleOperator::Contained,
358 value: rest.to_string(),
359 });
360 }
361 if let Some(rest) = value.strip_prefix("ov.") {
362 return Ok(Operation::Simple {
363 op: SimpleOperator::Overlap,
364 value: rest.to_string(),
365 });
366 }
367 if let Some(rest) = value.strip_prefix("sl.") {
368 return Ok(Operation::Simple {
369 op: SimpleOperator::StrictlyLeft,
370 value: rest.to_string(),
371 });
372 }
373 if let Some(rest) = value.strip_prefix("sr.") {
374 return Ok(Operation::Simple {
375 op: SimpleOperator::StrictlyRight,
376 value: rest.to_string(),
377 });
378 }
379 if let Some(rest) = value.strip_prefix("nxr.") {
380 return Ok(Operation::Simple {
381 op: SimpleOperator::NotExtendsRight,
382 value: rest.to_string(),
383 });
384 }
385 if let Some(rest) = value.strip_prefix("nxl.") {
386 return Ok(Operation::Simple {
387 op: SimpleOperator::NotExtendsLeft,
388 value: rest.to_string(),
389 });
390 }
391 if let Some(rest) = value.strip_prefix("adj.") {
392 return Ok(Operation::Simple {
393 op: SimpleOperator::Adjacent,
394 value: rest.to_string(),
395 });
396 }
397
398 if let Some(rest) = value.strip_prefix("in.") {
400 let values = parse_in_list(rest)?;
401 return Ok(Operation::In(values));
402 }
403
404 if let Some(rest) = value.strip_prefix("is.") {
406 let is_val = match rest {
407 "null" => IsValue::Null,
408 "true" => IsValue::True,
409 "false" => IsValue::False,
410 "unknown" => IsValue::Unknown,
411 _ => return Err(Error::InvalidQueryParam(format!("is.{}", rest))),
412 };
413 return Ok(Operation::Is(is_val));
414 }
415
416 if let Some(rest) = value.strip_prefix("isdistinct.") {
418 return Ok(Operation::IsDistinctFrom(rest.to_string()));
419 }
420
421 if let Some(rest) = value.strip_prefix("fts") {
423 return parse_fts(FtsOperator::Fts, rest);
424 }
425 if let Some(rest) = value.strip_prefix("plfts") {
426 return parse_fts(FtsOperator::Plain, rest);
427 }
428 if let Some(rest) = value.strip_prefix("phfts") {
429 return parse_fts(FtsOperator::Phrase, rest);
430 }
431 if let Some(rest) = value.strip_prefix("wfts") {
432 return parse_fts(FtsOperator::Websearch, rest);
433 }
434
435 Err(Error::InvalidQueryParam(value.into()))
436}
437
438fn parse_in_list(value: &str) -> Result<Vec<String>> {
440 let value = value
441 .strip_prefix('(')
442 .and_then(|s| s.strip_suffix(')'))
443 .ok_or_else(|| Error::InvalidQueryParam(format!("in.{}", value)))?;
444
445 Ok(value.split(',').map(|s| s.trim().to_string()).collect())
446}
447
448fn parse_fts(op: FtsOperator, rest: &str) -> Result<Operation> {
450 if let Some(rest) = rest.strip_prefix('(') {
451 let (lang, query) = rest
453 .split_once(").")
454 .ok_or_else(|| Error::InvalidQueryParam(format!("fts{}", rest)))?;
455 return Ok(Operation::Fts {
456 op,
457 language: Some(lang.to_string()),
458 value: query.to_string(),
459 });
460 }
461
462 let query = rest
463 .strip_prefix('.')
464 .ok_or_else(|| Error::InvalidQueryParam(format!("fts{}", rest)))?;
465 Ok(Operation::Fts {
466 op,
467 language: None,
468 value: query.to_string(),
469 })
470}
471
472fn parse_order_param(value: &str) -> Result<(EmbedPath, Vec<OrderTerm>)> {
478 let terms: Vec<OrderTerm> = value
479 .split(',')
480 .map(|s| parse_order_term(s.trim()))
481 .collect::<Result<Vec<_>>>()?;
482 Ok((vec![], terms))
483}
484
485fn parse_order_term(value: &str) -> Result<OrderTerm> {
486 let parts: Vec<&str> = value.split('.').collect();
487 if parts.is_empty() {
488 return Err(Error::InvalidQueryParam("order".into()));
489 }
490
491 let field_name = parts[0];
492 let mut direction = None;
493 let mut nulls = None;
494
495 for part in &parts[1..] {
496 match *part {
497 "asc" => direction = Some(OrderDirection::Asc),
498 "desc" => direction = Some(OrderDirection::Desc),
499 "nullsfirst" => nulls = Some(OrderNulls::First),
500 "nullslast" => nulls = Some(OrderNulls::Last),
501 _ => {}
502 }
503 }
504
505 Ok(OrderTerm::Field {
506 field: Field::simple(field_name),
507 direction,
508 nulls,
509 })
510}
511
512fn parse_logic_param(op: &str, value: &str) -> Result<LogicTree> {
518 let logic_op = match op {
519 "and" => LogicOperator::And,
520 "or" => LogicOperator::Or,
521 _ => return Err(Error::InvalidQueryParam(op.into())),
522 };
523
524 let value = value
526 .strip_prefix('(')
527 .and_then(|s| s.strip_suffix(')'))
528 .ok_or_else(|| Error::InvalidQueryParam(format!("{}={}", op, value)))?;
529
530 let children: Vec<LogicTree> = value
531 .split(',')
532 .map(|s| {
533 let (key, val) = s
534 .split_once('.')
535 .ok_or_else(|| Error::InvalidQueryParam(s.into()))?;
536 let (_, filter) = parse_filter_param(key, val)?;
537 Ok(LogicTree::Stmt(filter))
538 })
539 .collect::<Result<Vec<_>>>()?;
540
541 Ok(LogicTree::Expr {
542 negated: false,
543 op: logic_op,
544 children,
545 })
546}
547
548fn parse_identifier(input: &str) -> IResult<&str, &str> {
553 take_while1(|c: char| c.is_alphanumeric() || c == '_')(input)
554}
555
556fn parse_json_path(input: &str) -> IResult<&str, JsonPath> {
557 many0(alt((parse_arrow, parse_double_arrow)))(input)
558}
559
560fn parse_arrow(input: &str) -> IResult<&str, JsonOperation> {
561 let (input, _) = tag("->")(input)?;
562 let (input, operand) = alt((
563 map(digit1, |s: &str| {
564 JsonOperand::Idx(s.parse().unwrap_or(0))
565 }),
566 map(parse_identifier, |s| JsonOperand::Key(s.to_string())),
567 ))(input)?;
568 Ok((input, JsonOperation::Arrow(operand)))
569}
570
571fn parse_double_arrow(input: &str) -> IResult<&str, JsonOperation> {
572 let (input, _) = tag("->>")(input)?;
573 let (input, operand) = alt((
574 map(digit1, |s: &str| {
575 JsonOperand::Idx(s.parse().unwrap_or(0))
576 }),
577 map(parse_identifier, |s| JsonOperand::Key(s.to_string())),
578 ))(input)?;
579 Ok((input, JsonOperation::DoubleArrow(operand)))
580}
581
582#[cfg(test)]
583mod tests {
584 use super::*;
585
586 #[test]
587 fn test_parse_simple_filter() {
588 let params = parse_query_params("name=eq.John").unwrap();
589 assert_eq!(params.filters_root.len(), 1);
590 assert_eq!(params.filters_root[0].field.name, "name");
591 }
592
593 #[test]
594 fn test_parse_negated_filter() {
595 let params = parse_query_params("status=not.eq.active").unwrap();
596 assert!(params.filters_root[0].op_expr.negated);
597 }
598
599 #[test]
600 fn test_parse_in_filter() {
601 let params = parse_query_params("id=in.(1,2,3)").unwrap();
602 match ¶ms.filters_root[0].op_expr.operation {
603 Operation::In(values) => {
604 assert_eq!(values, &vec!["1", "2", "3"]);
605 }
606 _ => panic!("Expected In operation"),
607 }
608 }
609
610 #[test]
611 fn test_parse_is_null() {
612 let params = parse_query_params("deleted_at=is.null").unwrap();
613 match ¶ms.filters_root[0].op_expr.operation {
614 Operation::Is(IsValue::Null) => {}
615 _ => panic!("Expected Is Null"),
616 }
617 }
618
619 #[test]
620 fn test_parse_order() {
621 let params = parse_query_params("order=name.asc,age.desc.nullslast").unwrap();
622 assert_eq!(params.order.len(), 1);
623 let (_, terms) = ¶ms.order[0];
624 assert_eq!(terms.len(), 2);
625 }
626
627 #[test]
628 fn test_parse_limit_offset() {
629 let params = parse_query_params("limit=10&offset=20").unwrap();
630 let range = params.ranges.get("").unwrap();
631 assert_eq!(range.limit, Some(10));
632 assert_eq!(range.offset, 20);
633 }
634
635 #[test]
636 fn test_parse_select() {
637 let items = parse_select("id,name,orders(id,amount)").unwrap();
638 assert_eq!(items.len(), 3);
639 }
640
641 #[test]
642 fn test_parse_fts() {
643 let params = parse_query_params("content=fts(english).search+term").unwrap();
644 match ¶ms.filters_root[0].op_expr.operation {
645 Operation::Fts { op, language, value } => {
646 assert_eq!(*op, FtsOperator::Fts);
647 assert_eq!(language.as_deref(), Some("english"));
648 assert_eq!(value, "search+term");
649 }
650 _ => panic!("Expected FTS operation"),
651 }
652 }
653}