1use pest::Parser;
6use pest_derive::Parser;
7
8pub mod ast;
9pub mod executor;
10pub mod executor_utils;
11pub mod validator;
12
13#[derive(Parser)]
14#[grammar = "parser/grammar.pest"]
15pub struct AQLParser;
16
17use crate::error::{AqlError, ErrorCode, Result};
18use ast::*;
19use std::collections::HashMap;
20
21const RESERVED_KEYWORDS: &[&str] = &[
23 "query",
24 "mutation",
25 "subscription",
26 "fragment",
27 "on",
28 "transaction",
29 "schema",
30 "collection",
31 "type",
32 "enum",
33 "scalar",
34 "insertInto",
35 "insertMany",
36 "update",
37 "upsert",
38 "deleteFrom",
39 "enqueueJob",
40 "enqueueJobs",
41 "import",
42 "export",
43 "where",
44 "orderBy",
45 "limit",
46 "offset",
47 "first",
48 "last",
49 "after",
50 "before",
51 "search",
52 "validate",
53 "lookup",
54 "aggregate",
55 "groupBy",
56 "windowFunc",
57 "downsample",
58 "and",
59 "or",
60 "not",
61 "eq",
62 "ne",
63 "gt",
64 "gte",
65 "lt",
66 "lte",
67 "in",
68 "nin",
69 "contains",
70 "containsAny",
71 "containsAll",
72 "startsWith",
73 "endsWith",
74 "matches",
75 "isNull",
76 "isNotNull",
77 "near",
78 "within",
79 "true",
80 "false",
81 "null",
82 "ASC",
83 "DESC",
84];
85
86fn get_helpful_error_message(input: &str, line: usize, col: usize) -> String {
88 if let Some(line_content) = input.lines().nth(line.saturating_sub(1)) {
90 let chars: Vec<char> = line_content.chars().collect();
92 if col > 0 && col <= chars.len() {
93 let start = (0..col.saturating_sub(1))
95 .rev()
96 .find(|&i| !chars[i].is_alphanumeric() && chars[i] != '_')
97 .map(|i| i + 1)
98 .unwrap_or(0);
99
100 let end = (col.saturating_sub(1)..chars.len())
101 .find(|&i| !chars[i].is_alphanumeric() && chars[i] != '_')
102 .unwrap_or(chars.len());
103
104 let word: String = chars[start..end].iter().collect();
105
106 if RESERVED_KEYWORDS.contains(&word.as_str()) {
108 return format!(
109 "Syntax error at line {}, column {}: '{}' is a reserved keyword and cannot be used as a field name. \
110 Consider using a different name like '{}_value' or '{}Type'.",
111 line,
112 col,
113 word,
114 word.to_lowercase(),
115 word.to_lowercase()
116 );
117 }
118 }
119 }
120
121 format!("Syntax error at line {}, column {}", line, col)
123}
124
125pub fn parse(input: &str) -> Result<Document> {
127 let pairs = AQLParser::parse(Rule::document, input).map_err(|e| {
128 let (line, col) = match e.line_col {
129 pest::error::LineColLocation::Pos((l, c)) => (l, c),
130 pest::error::LineColLocation::Span((l, c), _) => (l, c),
131 };
132
133 #[cfg(debug_assertions)]
135 eprintln!("Parse error details: {}", e);
136
137 let user_message = get_helpful_error_message(input, line, col);
139
140 AqlError::new(ErrorCode::InvalidQuery, user_message).with_location(line, col)
141 })?;
142
143 let mut operations = Vec::new();
144
145 for pair in pairs {
146 if pair.as_rule() == Rule::document {
147 for inner in pair.into_inner() {
148 match inner.as_rule() {
149 Rule::operation => {
150 if let Some(op) = parse_operation(inner)? {
151 operations.push(op);
152 }
153 }
154 Rule::fragment_definition => {
155 operations.push(Operation::FragmentDefinition(parse_fragment_definition(
156 inner,
157 )?));
158 }
159 _ => {}
160 }
161 }
162 }
163 }
164
165 Ok(Document { operations })
166}
167
168pub fn parse_with_variables(input: &str, variables: &HashMap<String, Value>) -> Result<Document> {
170 let mut doc = parse(input)?;
171
172 for op in &mut doc.operations {
173 match op {
174 Operation::Query(q) => q.variables_values = variables.clone(),
175 Operation::Mutation(m) => m.variables_values = variables.clone(),
176 Operation::Subscription(s) => s.variables_values = variables.clone(),
177 _ => {}
178 }
179 }
180
181 Ok(doc)
182}
183
184fn parse_operation(pair: pest::iterators::Pair<Rule>) -> Result<Option<Operation>> {
185 match pair.as_rule() {
186 Rule::operation => {
187 for inner in pair.into_inner() {
188 return parse_operation(inner);
189 }
190 Ok(None)
191 }
192 Rule::query_operation => Ok(Some(Operation::Query(parse_query(pair)?))),
193 Rule::mutation_operation => Ok(Some(Operation::Mutation(parse_mutation(pair)?))),
194 Rule::subscription_operation => {
195 Ok(Some(Operation::Subscription(parse_subscription(pair)?)))
196 }
197 Rule::schema_operation => Ok(Some(Operation::Schema(parse_schema(pair)?))),
198 Rule::migration_operation => Ok(Some(Operation::Migration(parse_migration(pair)?))),
199 Rule::introspection_query => Ok(Some(Operation::Introspection(parse_introspection_query(
200 pair,
201 )?))),
202 Rule::handler_operation => Ok(Some(Operation::Handler(parse_handler_operation(pair)?))),
203 Rule::EOI => Ok(None),
204 _ => Ok(None),
205 }
206}
207
208fn parse_query(pair: pest::iterators::Pair<Rule>) -> Result<Query> {
209 let mut name = None;
210 let mut variable_definitions = Vec::new();
211 let mut directives = Vec::new();
212 let mut selection_set = Vec::new();
213
214 for inner in pair.into_inner() {
215 match inner.as_rule() {
216 Rule::identifier | Rule::keyword => name = Some(inner.as_str().to_string()),
217 Rule::variable_definitions => variable_definitions = parse_variable_definitions(inner)?,
218 Rule::directives => directives = parse_directives(inner)?,
219 Rule::selection_set => selection_set = parse_selection_set(inner)?,
220 _ => {}
221 }
222 }
223
224 Ok(Query {
225 name,
226 variable_definitions,
227 directives,
228 selection_set,
229 variables_values: HashMap::new(),
230 })
231}
232
233fn parse_mutation(pair: pest::iterators::Pair<Rule>) -> Result<Mutation> {
234 let mut name = None;
235 let mut variable_definitions = Vec::new();
236 let mut directives = Vec::new();
237 let mut operations = Vec::new();
238
239 for inner in pair.into_inner() {
240 match inner.as_rule() {
241 Rule::identifier | Rule::keyword => name = Some(inner.as_str().to_string()),
242 Rule::variable_definitions => variable_definitions = parse_variable_definitions(inner)?,
243 Rule::directives => directives = parse_directives(inner)?,
244 Rule::mutation_set => operations = parse_mutation_set(inner)?,
245 _ => {}
246 }
247 }
248
249 Ok(Mutation {
250 name,
251 variable_definitions,
252 directives,
253 operations,
254 variables_values: HashMap::new(),
255 })
256}
257
258fn parse_subscription(pair: pest::iterators::Pair<Rule>) -> Result<Subscription> {
259 let mut name = None;
260 let mut variable_definitions = Vec::new();
261 let mut directives = Vec::new();
262 let mut selection_set = Vec::new();
263
264 for inner in pair.into_inner() {
265 match inner.as_rule() {
266 Rule::identifier | Rule::keyword => name = Some(inner.as_str().to_string()),
267 Rule::variable_definitions => variable_definitions = parse_variable_definitions(inner)?,
268 Rule::directives => directives = parse_directives(inner)?,
269 Rule::subscription_set => selection_set = parse_subscription_set(inner)?,
270 _ => {}
271 }
272 }
273
274 Ok(Subscription {
275 name,
276 variable_definitions,
277 directives,
278 selection_set,
279 variables_values: HashMap::new(),
280 })
281}
282
283fn parse_schema(pair: pest::iterators::Pair<Rule>) -> Result<Schema> {
284 let mut operations = Vec::new();
285 for inner in pair.into_inner() {
286 if inner.as_rule() == Rule::schema_definition {
287 for rule in inner.into_inner() {
288 match rule.as_rule() {
289 Rule::define_collection => operations.push(parse_define_collection(rule)?),
290 Rule::alter_collection => operations.push(parse_alter_collection(rule)?),
291 Rule::drop_collection => operations.push(parse_drop_collection(rule)?),
292 _ => {}
293 }
294 }
295 }
296 }
297 Ok(Schema { operations })
298}
299
300fn parse_define_collection(pair: pest::iterators::Pair<Rule>) -> Result<SchemaOp> {
301 let mut name = String::new();
302 let mut if_not_exists = false;
303 let mut fields = Vec::new();
304 let mut directives = Vec::new();
305
306 for inner in pair.into_inner() {
307 match inner.as_rule() {
308 Rule::identifier | Rule::keyword => name = inner.as_str().to_string(),
309 Rule::field_definition => fields.push(parse_field_definition(inner)?),
310 Rule::directives => directives = parse_directives(inner)?,
311 _ => {
312 if inner.as_str() == "if" {
313 if_not_exists = true;
315 }
316 }
317 }
318 }
319 Ok(SchemaOp::DefineCollection {
320 name,
321 if_not_exists,
322 fields,
323 directives,
324 })
325}
326
327fn parse_alter_collection(pair: pest::iterators::Pair<Rule>) -> Result<SchemaOp> {
328 let mut name = String::new();
329 let mut actions = Vec::new();
330
331 for inner in pair.into_inner() {
332 match inner.as_rule() {
333 Rule::identifier | Rule::keyword => name = inner.as_str().to_string(),
334 Rule::alter_action => actions.push(parse_alter_action(inner)?),
335 _ => {}
336 }
337 }
338 Ok(SchemaOp::AlterCollection { name, actions })
339}
340
341fn parse_alter_action(pair: pest::iterators::Pair<Rule>) -> Result<AlterAction> {
342 let input_str = pair.as_str().to_string();
343 for inner in pair.into_inner() {
344 match inner.as_rule() {
345 Rule::add_action => {
346 let mut field_def = None;
347 let mut default = None;
348 for part in inner.into_inner() {
349 match part.as_rule() {
350 Rule::field_definition => field_def = Some(parse_field_definition(part)?),
351 Rule::value => default = Some(parse_value(part)?),
352 _ => {}
353 }
354 }
355 if let Some(f) = field_def {
356 return Ok(AlterAction::AddField { field: f, default });
357 }
358 }
359 Rule::drop_action => {
360 for id in inner.into_inner() {
361 if id.as_rule() == Rule::identifier || id.as_rule() == Rule::keyword {
362 return Ok(AlterAction::DropField(id.as_str().to_string()));
363 }
364 }
365 }
366 Rule::rename_action => {
367 let mut ids = Vec::new();
368 for id in inner.into_inner() {
369 if id.as_rule() == Rule::identifier || id.as_rule() == Rule::keyword {
370 ids.push(id.as_str().to_string());
371 }
372 }
373 if ids.len() == 2 {
374 return Ok(AlterAction::RenameField {
375 from: ids[0].clone(),
376 to: ids[1].clone(),
377 });
378 }
379 }
380 Rule::modify_action => {
381 for field in inner.into_inner() {
382 if field.as_rule() == Rule::field_definition {
383 return Ok(AlterAction::ModifyField(parse_field_definition(field)?));
384 }
385 }
386 }
387 _ => {}
388 }
389 }
390
391 Err(AqlError::new(
392 ErrorCode::InvalidQuery,
393 format!("Unknown alter action: {}", input_str),
394 ))
395}
396
397fn parse_drop_collection(pair: pest::iterators::Pair<Rule>) -> Result<SchemaOp> {
399 let mut name = String::new();
400 let mut if_exists = false;
401
402 for inner in pair.into_inner() {
403 match inner.as_rule() {
404 Rule::identifier | Rule::keyword => name = inner.as_str().to_string(),
405 _ => {
406 if inner.as_str() == "if" {
407 if_exists = true;
408 }
409 }
410 }
411 }
412 Ok(SchemaOp::DropCollection { name, if_exists })
413}
414
415fn parse_field_definition(pair: pest::iterators::Pair<Rule>) -> Result<FieldDef> {
416 let mut name = String::new();
417 let mut field_type = TypeAnnotation {
418 name: "String".to_string(),
419 is_array: false,
420 is_required: false,
421 };
422 let mut directives = Vec::new();
423
424 for inner in pair.into_inner() {
425 match inner.as_rule() {
426 Rule::identifier | Rule::keyword => name = inner.as_str().to_string(),
427 Rule::type_annotation => field_type = parse_type_annotation(inner)?,
428 Rule::directives => directives = parse_directives(inner)?,
429 _ => {}
430 }
431 }
432
433 Ok(FieldDef {
434 name,
435 field_type,
436 directives,
437 })
438}
439
440fn parse_migration(pair: pest::iterators::Pair<Rule>) -> Result<Migration> {
443 let mut steps = Vec::new();
444 for inner in pair.into_inner() {
445 if inner.as_rule() == Rule::migration_step {
446 steps.push(parse_migration_step(inner)?);
447 }
448 }
449 Ok(Migration { steps })
450}
451
452fn parse_migration_step(pair: pest::iterators::Pair<Rule>) -> Result<MigrationStep> {
453 let mut version = String::new();
454 let mut actions = Vec::new();
455
456 for inner in pair.into_inner() {
457 match inner.as_rule() {
458 Rule::migration_version => {
459 if let Some(inner_pair) = inner.into_inner().next() {
460 let raw = inner_pair.as_str();
461 version = if (raw.starts_with('"') && raw.ends_with('"'))
464 || (raw.starts_with('\'') && raw.ends_with('\''))
465 {
466 raw[1..raw.len() - 1].to_string()
467 } else {
468 raw.to_string()
469 };
470 }
471 }
472 Rule::migration_action => {
473 for act in inner.into_inner() {
474 match act.as_rule() {
475 Rule::define_collection => {
476 actions.push(MigrationAction::Schema(parse_define_collection(act)?))
477 }
478 Rule::alter_collection => {
479 actions.push(MigrationAction::Schema(parse_alter_collection(act)?))
480 }
481 Rule::drop_collection => {
482 actions.push(MigrationAction::Schema(parse_drop_collection(act)?))
483 }
484 Rule::data_migration => {
485 actions.push(MigrationAction::DataMigration(parse_data_migration(act)?))
486 }
487 _ => {}
488 }
489 }
490 }
491 _ => {}
492 }
493 }
494 Ok(MigrationStep { version, actions })
495}
496
497fn parse_data_migration(pair: pest::iterators::Pair<Rule>) -> Result<DataMigration> {
498 let mut collection = String::new();
499 let mut transforms = Vec::new();
500
501 for inner in pair.into_inner() {
502 match inner.as_rule() {
503 Rule::identifier | Rule::keyword => collection = inner.as_str().to_string(),
504 Rule::data_transform => transforms.push(parse_data_transform(inner)?),
505 _ => {}
506 }
507 }
508 Ok(DataMigration {
509 collection,
510 transforms,
511 })
512}
513
514fn parse_data_transform(pair: pest::iterators::Pair<Rule>) -> Result<DataTransform> {
515 let mut field = String::new();
516 let mut expression = String::new();
517 let mut filter = None;
518
519 for inner in pair.into_inner() {
520 match inner.as_rule() {
521 Rule::identifier | Rule::keyword => field = inner.as_str().to_string(),
522 Rule::expression => expression = inner.as_str().to_string(),
523 Rule::filter_object => {
524 let filter_value = parse_filter_object_to_value(inner)?;
526 filter = Some(value_to_filter(filter_value).map_err(|e| {
527 AqlError::new(
528 ErrorCode::FilterParseError,
529 format!("Failed to parse filter in data transform: {}", e),
530 )
531 })?);
532 }
533 _ => {}
534 }
535 }
536 Ok(DataTransform {
537 field,
538 expression,
539 filter,
540 })
541}
542
543fn parse_filter_object_to_value(pair: pest::iterators::Pair<Rule>) -> Result<Value> {
545 let mut map = HashMap::new();
546
547 for inner in pair.into_inner() {
548 if inner.as_rule() == Rule::filter_field_list {
549 for filter_field in inner.into_inner() {
550 if filter_field.as_rule() == Rule::filter_field {
551 parse_filter_field_to_map(filter_field, &mut map)?;
552 }
553 }
554 }
555 }
556
557 Ok(Value::Object(map))
558}
559
560fn parse_filter_field_to_map(
562 pair: pest::iterators::Pair<Rule>,
563 map: &mut HashMap<String, Value>,
564) -> Result<()> {
565 for inner in pair.into_inner() {
566 match inner.as_rule() {
567 Rule::logical_operator => {
568 parse_logical_operator_to_map(inner, map)?;
569 }
570 Rule::field_filter | Rule::nested_field_filter => {
571 let mut field_name = String::new();
572 let mut field_ops = HashMap::new();
573
574 for field_inner in inner.into_inner() {
575 match field_inner.as_rule() {
576 Rule::identifier | Rule::keyword => {
577 field_name = field_inner.as_str().to_string()
578 }
579 Rule::string => {
580 let s = field_inner.as_str();
581 field_name = s[1..s.len() - 1].to_string();
582 }
583 Rule::filter_condition => {
584 parse_filter_condition_to_map(field_inner, &mut field_ops)?;
585 }
586 _ => {}
587 }
588 }
589
590 if !field_name.is_empty() {
591 map.insert(field_name, Value::Object(field_ops));
592 }
593 }
594 _ => {}
595 }
596 }
597 Ok(())
598}
599
600fn parse_logical_operator_to_map(
602 pair: pest::iterators::Pair<Rule>,
603 map: &mut HashMap<String, Value>,
604) -> Result<()> {
605 for inner in pair.into_inner() {
606 match inner.as_rule() {
607 Rule::and_operator => {
608 let mut filters = Vec::new();
609 for and_inner in inner.into_inner() {
610 if and_inner.as_rule() == Rule::filter_object {
611 filters.push(parse_filter_object_to_value(and_inner)?);
612 }
613 }
614 map.insert("and".to_string(), Value::Array(filters));
615 }
616 Rule::or_operator => {
617 let mut filters = Vec::new();
618 for or_inner in inner.into_inner() {
619 if or_inner.as_rule() == Rule::filter_object {
620 filters.push(parse_filter_object_to_value(or_inner)?);
621 }
622 }
623 map.insert("or".to_string(), Value::Array(filters));
624 }
625 Rule::not_operator => {
626 for not_inner in inner.into_inner() {
627 if not_inner.as_rule() == Rule::filter_object {
628 map.insert("not".to_string(), parse_filter_object_to_value(not_inner)?);
629 }
630 }
631 }
632 _ => {}
633 }
634 }
635 Ok(())
636}
637
638fn parse_filter_condition_to_map(
640 pair: pest::iterators::Pair<Rule>,
641 map: &mut HashMap<String, Value>,
642) -> Result<()> {
643 for inner in pair.into_inner() {
644 if inner.as_rule() == Rule::filter_operator_list {
645 for op in inner.into_inner() {
646 if op.as_rule() == Rule::filter_operator {
647 parse_filter_operator_to_map(op, map)?;
648 }
649 }
650 }
651 }
652 Ok(())
653}
654
655fn parse_filter_operator_to_map(
657 pair: pest::iterators::Pair<Rule>,
658 map: &mut HashMap<String, Value>,
659) -> Result<()> {
660 for inner in pair.into_inner() {
661 match inner.as_rule() {
662 Rule::eq_operator => {
663 for val in inner.into_inner() {
664 if val.as_rule() == Rule::value {
665 map.insert("eq".to_string(), parse_value(val)?);
666 }
667 }
668 }
669 Rule::ne_operator => {
670 for val in inner.into_inner() {
671 if val.as_rule() == Rule::value {
672 map.insert("ne".to_string(), parse_value(val)?);
673 }
674 }
675 }
676 Rule::gt_operator => {
677 for val in inner.into_inner() {
678 if val.as_rule() == Rule::value {
679 map.insert("gt".to_string(), parse_value(val)?);
680 }
681 }
682 }
683 Rule::gte_operator => {
684 for val in inner.into_inner() {
685 if val.as_rule() == Rule::value {
686 map.insert("gte".to_string(), parse_value(val)?);
687 }
688 }
689 }
690 Rule::lt_operator => {
691 for val in inner.into_inner() {
692 if val.as_rule() == Rule::value {
693 map.insert("lt".to_string(), parse_value(val)?);
694 }
695 }
696 }
697 Rule::lte_operator => {
698 for val in inner.into_inner() {
699 if val.as_rule() == Rule::value {
700 map.insert("lte".to_string(), parse_value(val)?);
701 }
702 }
703 }
704 Rule::in_operator => {
705 for val in inner.into_inner() {
706 if val.as_rule() == Rule::array {
707 map.insert("in".to_string(), parse_value(val)?);
708 }
709 }
710 }
711 Rule::nin_operator => {
712 for val in inner.into_inner() {
713 if val.as_rule() == Rule::array {
714 map.insert("nin".to_string(), parse_value(val)?);
715 }
716 }
717 }
718 Rule::contains_operator => {
719 for val in inner.into_inner() {
720 if val.as_rule() == Rule::value {
721 map.insert("contains".to_string(), parse_value(val)?);
722 }
723 }
724 }
725 Rule::starts_with_operator => {
726 for val in inner.into_inner() {
727 if val.as_rule() == Rule::value {
728 map.insert("startsWith".to_string(), parse_value(val)?);
729 }
730 }
731 }
732 Rule::ends_with_operator => {
733 for val in inner.into_inner() {
734 if val.as_rule() == Rule::value {
735 map.insert("endsWith".to_string(), parse_value(val)?);
736 }
737 }
738 }
739 Rule::contains_any_operator => {
740 for val in inner.into_inner() {
741 if val.as_rule() == Rule::array {
742 map.insert("containsAny".to_string(), parse_value(val)?);
743 }
744 }
745 }
746 Rule::contains_all_operator => {
747 for val in inner.into_inner() {
748 if val.as_rule() == Rule::array {
749 map.insert("containsAll".to_string(), parse_value(val)?);
750 }
751 }
752 }
753 Rule::matches_operator => {
754 for val in inner.into_inner() {
755 if val.as_rule() == Rule::value {
756 map.insert("matches".to_string(), parse_value(val)?);
757 }
758 }
759 }
760 Rule::increment_operator => {
761 for val in inner.into_inner() {
762 if val.as_rule() == Rule::number {
763 map.insert("increment".to_string(), parse_value(val)?);
764 }
765 }
766 }
767 Rule::decrement_operator => {
768 for val in inner.into_inner() {
769 if val.as_rule() == Rule::number {
770 map.insert("decrement".to_string(), parse_value(val)?);
771 }
772 }
773 }
774 Rule::push_operator => {
775 for val in inner.into_inner() {
776 if val.as_rule() == Rule::value {
777 map.insert("push".to_string(), parse_value(val)?);
778 }
779 }
780 }
781 Rule::pull_operator => {
782 for val in inner.into_inner() {
783 if val.as_rule() == Rule::value {
784 map.insert("pull".to_string(), parse_value(val)?);
785 }
786 }
787 }
788 Rule::add_to_set_operator => {
789 for val in inner.into_inner() {
790 if val.as_rule() == Rule::value {
791 map.insert("addToSet".to_string(), parse_value(val)?);
792 }
793 }
794 }
795 Rule::is_null_operator => {
796 map.insert("isNull".to_string(), Value::Boolean(true));
797 }
798 Rule::is_not_null_operator => {
799 map.insert("isNotNull".to_string(), Value::Boolean(true));
800 }
801 _ => {}
802 }
803 }
804 Ok(())
805}
806
807fn parse_variable_definitions(
808 pair: pest::iterators::Pair<Rule>,
809) -> Result<Vec<VariableDefinition>> {
810 let mut definitions = Vec::new();
811 for inner in pair.into_inner() {
812 if inner.as_rule() == Rule::variable_definition {
813 definitions.push(parse_variable_definition(inner)?);
814 }
815 }
816 Ok(definitions)
817}
818
819fn parse_variable_definition(pair: pest::iterators::Pair<Rule>) -> Result<VariableDefinition> {
820 let mut name = String::new();
821 let mut var_type = TypeAnnotation {
822 name: "String".to_string(),
823 is_array: false,
824 is_required: false,
825 };
826 let mut default_value = None;
827
828 for inner in pair.into_inner() {
829 match inner.as_rule() {
830 Rule::variable => name = inner.as_str().trim_start_matches('$').to_string(),
831 Rule::type_annotation => var_type = parse_type_annotation(inner)?,
832 Rule::default_value => {
833 for val in inner.into_inner() {
834 if val.as_rule() == Rule::value {
835 default_value = Some(parse_value(val)?);
836 }
837 }
838 }
839 _ => {}
840 }
841 }
842
843 Ok(VariableDefinition {
844 name,
845 var_type,
846 default_value,
847 })
848}
849
850fn parse_type_annotation(pair: pest::iterators::Pair<Rule>) -> Result<TypeAnnotation> {
851 let mut name = String::new();
852 let mut is_array = false;
853 let mut is_required = false;
854
855 for inner in pair.into_inner() {
856 match inner.as_rule() {
857 Rule::type_name => name = inner.as_str().to_string(),
858 Rule::array_type => {
859 is_array = true;
860 for arr_inner in inner.into_inner() {
861 if arr_inner.as_rule() == Rule::type_annotation {
862 let inner_type = parse_type_annotation(arr_inner)?;
863 name = inner_type.name;
864 }
865 }
866 }
867 Rule::type_modifier => is_required = true,
868 _ => name = inner.as_str().to_string(),
869 }
870 }
871
872 Ok(TypeAnnotation {
873 name,
874 is_array,
875 is_required,
876 })
877}
878
879fn parse_directives(pair: pest::iterators::Pair<Rule>) -> Result<Vec<Directive>> {
880 let mut directives = Vec::new();
881 for inner in pair.into_inner() {
882 if inner.as_rule() == Rule::directive {
883 directives.push(parse_directive(inner)?);
884 }
885 }
886 Ok(directives)
887}
888
889fn parse_directive(pair: pest::iterators::Pair<Rule>) -> Result<Directive> {
890 let mut name = String::new();
891 let mut arguments = Vec::new();
892
893 for inner in pair.into_inner() {
894 match inner.as_rule() {
895 Rule::directive_name | Rule::identifier | Rule::keyword => {
896 name = inner.as_str().to_string()
897 }
898 Rule::arguments => arguments = parse_arguments(inner)?,
899 _ => {}
900 }
901 }
902
903 Ok(Directive { name, arguments })
904}
905
906fn parse_selection_set(pair: pest::iterators::Pair<Rule>) -> Result<Vec<Selection>> {
907 let mut selections = Vec::new();
908 for inner in pair.into_inner() {
909 if inner.as_rule() == Rule::field {
910 selections.push(parse_selection(inner)?);
911 }
912 }
913 Ok(selections)
914}
915
916fn parse_subscription_set(pair: pest::iterators::Pair<Rule>) -> Result<Vec<Selection>> {
917 let mut selections = Vec::new();
918 for inner in pair.into_inner() {
919 if inner.as_rule() == Rule::subscription_field {
920 selections.push(parse_selection(inner)?);
921 }
922 }
923 Ok(selections)
924}
925
926fn parse_selection(pair: pest::iterators::Pair<Rule>) -> Result<Selection> {
928 let inner = pair.clone().into_inner().next().unwrap();
933
934 match inner.as_rule() {
935 Rule::fragment_spread => {
936 let name = inner.into_inner().next().unwrap().as_str().to_string();
937 return Ok(Selection::FragmentSpread(name));
938 }
939 Rule::inline_fragment => {
940 let mut type_condition = String::new();
941 let mut selection_set = Vec::new();
942 for child in inner.into_inner() {
943 match child.as_rule() {
944 Rule::identifier | Rule::keyword => type_condition = child.as_str().to_string(),
945 Rule::selection_set => selection_set = parse_selection_set(child)?,
946 _ => {}
947 }
948 }
949 return Ok(Selection::InlineFragment(ast::InlineFragment {
950 type_condition,
951 selection_set,
952 }));
953 }
954 _ => {
955 return Ok(Selection::Field(parse_field_inner(pair)?));
957 }
958 }
959}
960
961fn parse_field_inner(pair: pest::iterators::Pair<Rule>) -> Result<Field> {
963 let mut alias = None;
964 let mut name = String::new();
965 let mut arguments = Vec::new();
966 let mut directives = Vec::new();
967 let mut selection_set = Vec::new();
968
969 for inner in pair.into_inner() {
970 match inner.as_rule() {
971 Rule::alias_name => {
972 for alias_inner in inner.into_inner() {
973 if alias_inner.as_rule() == Rule::identifier {
974 alias = Some(alias_inner.as_str().to_string());
975 }
976 }
977 }
978 Rule::identifier | Rule::keyword => name = inner.as_str().to_string(),
979 Rule::lookup_selection => {
980 name = "lookup".to_string();
981 for sel_inner in inner.clone().into_inner() {
983 if sel_inner.as_rule() == Rule::alias_name {
984 for alias_inner in sel_inner.into_inner() {
985 if alias_inner.as_rule() == Rule::identifier
986 || alias_inner.as_rule() == Rule::keyword
987 {
988 alias = Some(alias_inner.as_str().to_string());
989 }
990 }
991 }
992 }
993 let lookup = parse_lookup_selection(inner)?;
994 arguments.push(ast::Argument {
995 name: "collection".to_string(),
996 value: ast::Value::String(lookup.collection),
997 });
998 arguments.push(ast::Argument {
999 name: "localField".to_string(),
1000 value: ast::Value::String(lookup.local_field),
1001 });
1002 arguments.push(ast::Argument {
1003 name: "foreignField".to_string(),
1004 value: ast::Value::String(lookup.foreign_field),
1005 });
1006 if let Some(filter) = lookup.filter {
1007 arguments.push(ast::Argument {
1008 name: "where".to_string(),
1009 value: filter_to_value(&filter),
1010 });
1011 }
1012 selection_set = lookup.selection_set;
1013 }
1014 Rule::computed_field => {
1015 name = "__compute__".to_string();
1016 for cf_inner in inner.into_inner() {
1017 match cf_inner.as_rule() {
1018 Rule::identifier | Rule::keyword => {
1019 alias = Some(cf_inner.as_str().to_string())
1020 }
1021 Rule::computed_expression => {
1022 let expr_str = cf_inner.as_str().trim();
1024 let val = if expr_str.starts_with('"') && expr_str.ends_with('"') {
1026 expr_str[1..expr_str.len() - 1].to_string()
1027 } else {
1028 expr_str.to_string()
1029 };
1030 arguments.push(ast::Argument {
1031 name: "expr".to_string(),
1032 value: ast::Value::String(val),
1033 });
1034 }
1035 _ => {}
1036 }
1037 }
1038 }
1039 Rule::window_function => {
1040 name = "windowFunc".to_string();
1041 let mut parts = inner.into_inner();
1042 if let Some(alias_pair) = parts.next() {
1043 if alias_pair.as_rule() == Rule::identifier
1044 || alias_pair.as_rule() == Rule::keyword
1045 {
1046 alias = Some(alias_pair.as_str().to_string());
1047 }
1048 }
1049 for part in parts {
1050 if part.as_rule() == Rule::window_args {
1051 let mut strings: Vec<String> = Vec::new();
1052 let mut wsize: i64 = 3;
1053 for arg in part.into_inner() {
1054 match arg.as_rule() {
1055 Rule::string => {
1056 let s = arg.as_str();
1057 strings.push(parse_string(s));
1058 }
1059 Rule::number => {
1060 wsize = arg.as_str().parse().unwrap_or(3);
1061 }
1062 _ => {}
1063 }
1064 }
1065 if strings.len() >= 2 {
1066 arguments.push(ast::Argument {
1067 name: "field".to_string(),
1068 value: ast::Value::String(strings[0].clone()),
1069 });
1070 arguments.push(ast::Argument {
1071 name: "function".to_string(),
1072 value: ast::Value::String(strings[1].clone()),
1073 });
1074 }
1075 arguments.push(ast::Argument {
1076 name: "windowSize".to_string(),
1077 value: ast::Value::Int(wsize),
1078 });
1079 }
1080 }
1081 }
1082 Rule::arguments => arguments = parse_arguments(inner)?,
1083 Rule::directives => directives = parse_directives(inner)?,
1084 Rule::sub_selection => {
1085 for sel in inner.into_inner() {
1086 if sel.as_rule() == Rule::selection_set {
1087 selection_set = parse_selection_set(sel)?;
1088 }
1089 }
1090 }
1091 Rule::aggregate_with_alias => {
1092 name = "aggregate".to_string();
1093 for sel in inner.into_inner() {
1094 match sel.as_rule() {
1095 Rule::alias_name => {
1096 for alias_inner in sel.into_inner() {
1097 if alias_inner.as_rule() == Rule::identifier {
1098 alias = Some(alias_inner.as_str().to_string());
1099 }
1100 }
1101 }
1102 Rule::aggregate_field_list => {
1103 for agg_field in sel.into_inner() {
1104 if agg_field.as_rule() == Rule::aggregate_field {
1105 selection_set
1106 .push(Selection::Field(parse_aggregate_field(agg_field)?));
1107 }
1108 }
1109 }
1110 _ => {}
1111 }
1112 }
1113 }
1114 Rule::special_field_selection => {
1115 for sel in inner.into_inner() {
1116 match sel.as_rule() {
1117 Rule::alias_name => {
1118 for alias_inner in sel.into_inner() {
1119 if alias_inner.as_rule() == Rule::identifier {
1120 alias = Some(alias_inner.as_str().to_string());
1121 }
1122 }
1123 }
1124 Rule::group_by_selection => {
1125 name = "groupBy".to_string();
1126 for gb_inner in sel.into_inner() {
1127 match gb_inner.as_rule() {
1128 Rule::group_by_args => {
1129 for arg in gb_inner.into_inner() {
1130 match arg.as_rule() {
1131 Rule::string => {
1132 let s = arg.as_str();
1133 arguments.push(ast::Argument {
1134 name: "field".to_string(),
1135 value: ast::Value::String(parse_string(s)),
1136 });
1137 }
1138 Rule::array => {
1139 arguments.push(ast::Argument {
1140 name: "fields".to_string(),
1141 value: parse_value(arg)?,
1142 });
1143 }
1144 _ => {}
1145 }
1146 }
1147 }
1148 Rule::group_by_fields => {
1149 for field in gb_inner.into_inner() {
1150 if field.as_rule() == Rule::group_by_field {
1151 let mut alias = None;
1152 let mut name = String::new();
1153 let mut is_aggregate = false;
1154 let mut agg_field = None;
1155
1156 for f_inner in field.clone().into_inner() {
1157 match f_inner.as_rule() {
1158 Rule::alias_name => {
1159 for alias_inner in f_inner.into_inner()
1160 {
1161 if alias_inner.as_rule()
1162 == Rule::identifier
1163 || alias_inner.as_rule()
1164 == Rule::keyword
1165 {
1166 alias = Some(
1167 alias_inner
1168 .as_str()
1169 .to_string(),
1170 );
1171 }
1172 }
1173 }
1174 Rule::identifier | Rule::keyword => {
1175 name = f_inner.as_str().to_string();
1176 }
1177 Rule::aggregate_with_alias => {
1178 is_aggregate = true;
1179 let agg_name = "aggregate".to_string();
1180 let mut agg_alias = None;
1181 let mut agg_selection_set = Vec::new();
1182 for sel in f_inner.into_inner() {
1183 match sel.as_rule() {
1184 Rule::alias_name => {
1185 for alias_inner in
1186 sel.into_inner()
1187 {
1188 if alias_inner.as_rule()
1189 == Rule::identifier
1190 {
1191 agg_alias = Some(
1192 alias_inner
1193 .as_str()
1194 .to_string(
1195 ),
1196 );
1197 }
1198 }
1199 }
1200 Rule::aggregate_field_list => {
1201 for agg_f in
1202 sel.into_inner()
1203 {
1204 if agg_f.as_rule() == Rule::aggregate_field {
1205 if let Ok(af) = parse_aggregate_field(agg_f) {
1206 agg_selection_set.push(Selection::Field(af));
1207 }
1208 }
1209 }
1210 }
1211 _ => {}
1212 }
1213 }
1214 agg_field = Some(Field {
1215 alias: agg_alias,
1216 name: agg_name,
1217 arguments: Vec::new(),
1218 directives: Vec::new(),
1219 selection_set: agg_selection_set,
1220 computed_expression: None,
1221 });
1222 }
1223 _ => {}
1224 }
1225 }
1226
1227 if is_aggregate {
1228 if let Some(f) = agg_field {
1229 selection_set.push(Selection::Field(f));
1230 }
1231 } else {
1232 if name.is_empty() {
1233 let raw = field.as_str().trim();
1234 let actual_name = if let Some(colon_idx) =
1235 raw.find(':')
1236 {
1237 raw[colon_idx + 1..].trim().to_string()
1238 } else {
1239 raw.to_string()
1240 };
1241 name = actual_name;
1242 }
1243 selection_set.push(Selection::Field(Field {
1244 alias,
1245 name,
1246 arguments: Vec::new(),
1247 directives: Vec::new(),
1248 selection_set: Vec::new(),
1249 computed_expression: None,
1250 }));
1251 }
1252 }
1253 }
1254 }
1255 _ => {}
1256 }
1257 }
1258 }
1259 Rule::lookup_selection => {
1260 name = "lookup".to_string();
1261 let lookup = parse_lookup_selection(sel)?;
1262 arguments.push(ast::Argument {
1263 name: "collection".to_string(),
1264 value: ast::Value::String(lookup.collection),
1265 });
1266 arguments.push(ast::Argument {
1267 name: "localField".to_string(),
1268 value: ast::Value::String(lookup.local_field),
1269 });
1270 arguments.push(ast::Argument {
1271 name: "foreignField".to_string(),
1272 value: ast::Value::String(lookup.foreign_field),
1273 });
1274 if let Some(filter) = lookup.filter {
1275 arguments.push(ast::Argument {
1276 name: "where".to_string(),
1277 value: filter_to_value(&filter),
1278 });
1279 }
1280 selection_set = lookup.selection_set;
1281 }
1282 Rule::page_info_selection => {
1283 name = "pageInfo".to_string();
1284 }
1285 Rule::edges_selection => {
1286 name = "edges".to_string();
1287 for edge_inner in sel.into_inner() {
1288 if edge_inner.as_rule() == Rule::edge_fields {
1289 for edge_field in edge_inner.into_inner() {
1290 if edge_field.as_rule() == Rule::edge_field {
1291 let edge_str = edge_field.as_str().trim();
1292 if edge_str.starts_with("cursor") {
1293 selection_set.push(Selection::Field(Field {
1294 alias: None,
1295 name: "cursor".to_string(),
1296 arguments: Vec::new(),
1297 directives: Vec::new(),
1298 selection_set: Vec::new(),
1299 computed_expression: None,
1300 }));
1301 } else if edge_str.starts_with("node") {
1302 let mut node_selection = Vec::new();
1303 for node_inner in edge_field.into_inner() {
1304 if node_inner.as_rule() == Rule::selection_set {
1305 node_selection =
1306 parse_selection_set(node_inner)?;
1307 }
1308 }
1309 selection_set.push(Selection::Field(Field {
1310 alias: None,
1311 name: "node".to_string(),
1312 arguments: Vec::new(),
1313 directives: Vec::new(),
1314 selection_set: node_selection,
1315 computed_expression: None,
1316 }));
1317 }
1318 }
1319 }
1320 }
1321 }
1322 }
1323 Rule::downsample_selection => {
1324 name = "downsample".to_string();
1325 for ds_inner in sel.into_inner() {
1326 match ds_inner.as_rule() {
1327 Rule::downsample_args => {
1328 let mut strings = Vec::new();
1329 for a in ds_inner.into_inner() {
1330 if a.as_rule() == Rule::string {
1331 let s = a.as_str();
1332 strings.push(s[1..s.len() - 1].to_string());
1333 }
1334 }
1335 if strings.len() >= 2 {
1336 arguments.push(ast::Argument {
1337 name: "interval".to_string(),
1338 value: ast::Value::String(strings[0].clone()),
1339 });
1340 arguments.push(ast::Argument {
1341 name: "aggregation".to_string(),
1342 value: ast::Value::String(strings[1].clone()),
1343 });
1344 }
1345 }
1346 Rule::sub_selection => {
1347 for sub in ds_inner.into_inner() {
1348 if sub.as_rule() == Rule::selection_set {
1349 selection_set = parse_selection_set(sub)?;
1350 }
1351 }
1352 }
1353 _ => {}
1354 }
1355 }
1356 }
1357 Rule::window_function => {
1358 let mut parts = sel.into_inner();
1360 if let Some(alias_pair) = parts.next() {
1362 if alias_pair.as_rule() == Rule::identifier {
1363 alias = Some(alias_pair.as_str().to_string());
1364 }
1365 }
1366 name = "windowFunc".to_string();
1367 for part in parts {
1368 if part.as_rule() == Rule::window_args {
1369 let mut strings: Vec<String> = Vec::new();
1370 let mut wsize: i64 = 3;
1371 for arg in part.into_inner() {
1372 match arg.as_rule() {
1373 Rule::string => {
1374 let s = arg.as_str();
1375 strings.push(s[1..s.len() - 1].to_string());
1376 }
1377 Rule::number => {
1378 wsize = arg.as_str().parse().unwrap_or(3);
1379 }
1380 _ => {}
1381 }
1382 }
1383 if strings.len() >= 2 {
1385 arguments.push(ast::Argument {
1386 name: "field".to_string(),
1387 value: ast::Value::String(strings[0].clone()),
1388 });
1389 arguments.push(ast::Argument {
1390 name: "function".to_string(),
1391 value: ast::Value::String(strings[1].clone()),
1392 });
1393 }
1394 arguments.push(ast::Argument {
1395 name: "windowSize".to_string(),
1396 value: ast::Value::Int(wsize),
1397 });
1398 }
1399 }
1400 }
1401 _ => {}
1402 }
1403 }
1404 }
1405 _ => {}
1406 }
1407 }
1408 Ok(Field {
1409 alias,
1410 name,
1411 arguments,
1412 directives,
1413 selection_set,
1414 computed_expression: None,
1415 })
1416}
1417
1418fn parse_aggregate_field(pair: pest::iterators::Pair<Rule>) -> Result<Field> {
1421 let mut name = String::new();
1422 let mut alias = None;
1423 let mut arguments = Vec::new();
1424 let pair_str = pair.as_str().to_string();
1425
1426 for inner in pair.into_inner() {
1427 match inner.as_rule() {
1428 Rule::aggregate_field_alias => {
1429 for alias_inner in inner.into_inner() {
1431 if alias_inner.as_rule() == Rule::identifier {
1432 alias = Some(alias_inner.as_str().to_string());
1433 }
1434 }
1435 }
1436 Rule::aggregate_field_value => {
1437 let inner_str = inner.as_str().to_string();
1439 for val_inner in inner.into_inner() {
1440 match val_inner.as_rule() {
1441 Rule::aggregate_function => {
1442 for fn_inner in val_inner.into_inner() {
1444 match fn_inner.as_rule() {
1445 Rule::aggregate_name => {
1446 name = fn_inner.as_str().to_string();
1447 }
1448 Rule::aggregate_args => {
1449 let mut arg_name = String::new();
1451 let mut arg_value = Value::Null;
1452
1453 for arg_inner in fn_inner.into_inner() {
1454 match arg_inner.as_rule() {
1455 Rule::string => {
1456 let s = arg_inner.as_str();
1457 arg_value = Value::String(
1458 s[1..s.len() - 1].to_string(),
1459 );
1460 if arg_name.is_empty() {
1461 arg_name = "field".to_string();
1462 }
1463 }
1464 Rule::array => {
1465 arg_value = parse_value(arg_inner)?;
1466 arg_name = "fields".to_string();
1467 }
1468 _ => {
1469 let text = arg_inner.as_str();
1471 if text == "field" || text == "fields" {
1472 arg_name = text.to_string();
1473 }
1474 }
1475 }
1476 }
1477
1478 if !arg_name.is_empty() && !matches!(arg_value, Value::Null)
1479 {
1480 arguments.push(Argument {
1481 name: arg_name,
1482 value: arg_value,
1483 });
1484 }
1485 }
1486 _ => {}
1487 }
1488 }
1489 }
1490 _ => {
1491 }
1493 }
1494 }
1495
1496 if name.is_empty() {
1498 let text = inner_str.trim();
1499 if text == "count" {
1500 name = "count".to_string();
1501 }
1502 }
1503 }
1504 _ => {}
1505 }
1506 }
1507
1508 if name.is_empty() {
1510 let text = pair_str.trim();
1511 if text == "count" || text.ends_with(": count") || text.contains("count") {
1512 name = "count".to_string();
1513 }
1514 }
1515 Ok(Field {
1516 alias,
1517 name,
1518 arguments,
1519 directives: vec![],
1520 selection_set: vec![],
1521 computed_expression: None,
1522 })
1523}
1524
1525fn parse_mutation_set(pair: pest::iterators::Pair<Rule>) -> Result<Vec<MutationOperation>> {
1526 let mut operations = Vec::new();
1527 for inner in pair.into_inner() {
1528 if inner.as_rule() == Rule::mutation_field {
1529 operations.push(parse_mutation_field(inner)?);
1530 }
1531 }
1532 Ok(operations)
1533}
1534
1535fn parse_mutation_field(pair: pest::iterators::Pair<Rule>) -> Result<MutationOperation> {
1536 let mut alias = None;
1537 let mut operation = MutationOp::Insert {
1538 collection: String::new(),
1539 data: Value::Null,
1540 };
1541 let mut directives = Vec::new();
1542 let mut selection_set = Vec::new();
1543
1544 for inner in pair.into_inner() {
1545 match inner.as_rule() {
1546 Rule::alias_name => {
1547 for alias_inner in inner.into_inner() {
1548 if alias_inner.as_rule() == Rule::identifier {
1549 alias = Some(alias_inner.as_str().to_string());
1550 }
1551 }
1552 }
1553 Rule::mutation_call => {
1554 let (op, sel) = parse_mutation_call(inner)?;
1555 operation = op;
1556 selection_set = sel;
1557 }
1558 Rule::directives => directives = parse_directives(inner)?,
1559 _ => {}
1560 }
1561 }
1562
1563 Ok(MutationOperation {
1564 alias,
1565 operation,
1566 directives,
1567 selection_set,
1568 })
1569}
1570
1571fn parse_mutation_call(pair: pest::iterators::Pair<Rule>) -> Result<(MutationOp, Vec<Selection>)> {
1572 for inner in pair.into_inner() {
1573 match inner.as_rule() {
1574 Rule::insert_mutation => return parse_insert_mutation(inner),
1575 Rule::insert_many_mutation => return parse_insert_many_mutation(inner),
1576 Rule::update_mutation => return parse_update_mutation(inner),
1577 Rule::upsert_mutation => return parse_upsert_mutation(inner),
1578 Rule::delete_mutation => return parse_delete_mutation(inner),
1579 Rule::enqueue_job_mutation => return parse_enqueue_job_mutation(inner),
1580 Rule::enqueue_jobs_mutation => return parse_enqueue_jobs_mutation(inner),
1581 Rule::import_mutation => return parse_import_mutation(inner),
1582 Rule::export_mutation => return parse_export_mutation(inner),
1583 Rule::transaction_block => return parse_transaction_block(inner),
1584 _ => {}
1585 }
1586 }
1587 Err(AqlError::new(
1588 ErrorCode::InvalidQuery,
1589 "Unknown mutation type".to_string(),
1590 ))
1591}
1592
1593fn parse_insert_mutation(
1594 pair: pest::iterators::Pair<Rule>,
1595) -> Result<(MutationOp, Vec<Selection>)> {
1596 let mut collection = String::new();
1597 let mut data = Value::Null;
1598 let mut selection_set = Vec::new();
1599
1600 for inner in pair.into_inner() {
1601 match inner.as_rule() {
1602 Rule::insert_args => {
1603 for arg in parse_arguments_list(inner)? {
1604 match arg.name.as_str() {
1605 "collection" => {
1606 if let Value::String(s) = arg.value {
1607 collection = s;
1608 }
1609 }
1610 "data" => data = arg.value,
1611 _ => {}
1612 }
1613 }
1614 }
1615 Rule::sub_selection => {
1616 for sel in inner.into_inner() {
1617 if sel.as_rule() == Rule::selection_set {
1618 selection_set = parse_selection_set(sel)?;
1619 }
1620 }
1621 }
1622 _ => {}
1623 }
1624 }
1625
1626 Ok((MutationOp::Insert { collection, data }, selection_set))
1627}
1628
1629fn parse_insert_many_mutation(
1630 pair: pest::iterators::Pair<Rule>,
1631) -> Result<(MutationOp, Vec<Selection>)> {
1632 let mut collection = String::new();
1633 let mut data = Value::Array(Vec::new());
1634 let mut selection_set = Vec::new();
1635
1636 for inner in pair.into_inner() {
1637 match inner.as_rule() {
1638 Rule::insert_many_args => {
1639 for arg in parse_arguments_list(inner)? {
1640 match arg.name.as_str() {
1641 "collection" => {
1642 if let Value::String(s) = arg.value {
1643 collection = s;
1644 }
1645 }
1646 "data" => {
1647 data = arg.value;
1648 }
1649 _ => {}
1650 }
1651 }
1652 }
1653 Rule::sub_selection => {
1654 for sel in inner.into_inner() {
1655 if sel.as_rule() == Rule::selection_set {
1656 selection_set = parse_selection_set(sel)?;
1657 }
1658 }
1659 }
1660 _ => {}
1661 }
1662 }
1663
1664 Ok((MutationOp::InsertMany { collection, data }, selection_set))
1665}
1666
1667fn parse_upsert_mutation(
1668 pair: pest::iterators::Pair<Rule>,
1669) -> Result<(MutationOp, Vec<Selection>)> {
1670 let mut collection = String::new();
1671 let mut filter = None;
1672 let mut data = Value::Null;
1673 let mut selection_set = Vec::new();
1674
1675 for inner in pair.into_inner() {
1676 match inner.as_rule() {
1677 Rule::upsert_args => {
1678 for arg in parse_arguments_list(inner)? {
1679 match arg.name.as_str() {
1680 "collection" => {
1681 if let Value::String(s) = arg.value {
1682 collection = s;
1683 }
1684 }
1685 "where" => filter = Some(value_to_filter(arg.value)?),
1686 "data" | "set" => data = arg.value,
1687 _ => {}
1688 }
1689 }
1690 }
1691 Rule::sub_selection => {
1692 for sel in inner.into_inner() {
1693 if sel.as_rule() == Rule::selection_set {
1694 selection_set = parse_selection_set(sel)?;
1695 }
1696 }
1697 }
1698 _ => {}
1699 }
1700 }
1701
1702 Ok((
1703 MutationOp::Upsert {
1704 collection,
1705 filter,
1706 data,
1707 },
1708 selection_set,
1709 ))
1710}
1711
1712fn parse_update_mutation(
1713 pair: pest::iterators::Pair<Rule>,
1714) -> Result<(MutationOp, Vec<Selection>)> {
1715 let mut collection = String::new();
1716 let mut filter = None;
1717 let mut data = Value::Null;
1718 let mut selection_set = Vec::new();
1719
1720 for inner in pair.into_inner() {
1721 match inner.as_rule() {
1722 Rule::update_args => {
1723 for arg in parse_arguments_list(inner)? {
1724 match arg.name.as_str() {
1725 "collection" => {
1726 if let Value::String(s) = arg.value {
1727 collection = s;
1728 }
1729 }
1730 "where" => filter = Some(value_to_filter(arg.value)?),
1731 "data" | "set" => data = arg.value,
1732 _ => {}
1733 }
1734 }
1735 }
1736 Rule::sub_selection => {
1737 for sel in inner.into_inner() {
1738 if sel.as_rule() == Rule::selection_set {
1739 selection_set = parse_selection_set(sel)?;
1740 }
1741 }
1742 }
1743 _ => {}
1744 }
1745 }
1746
1747 Ok((
1748 MutationOp::Update {
1749 collection,
1750 filter,
1751 data,
1752 },
1753 selection_set,
1754 ))
1755}
1756
1757fn parse_delete_mutation(
1758 pair: pest::iterators::Pair<Rule>,
1759) -> Result<(MutationOp, Vec<Selection>)> {
1760 let mut collection = String::new();
1761 let mut filter = None;
1762 let mut selection_set = Vec::new();
1763
1764 for inner in pair.into_inner() {
1765 match inner.as_rule() {
1766 Rule::delete_args => {
1767 for arg in parse_arguments_list(inner)? {
1768 match arg.name.as_str() {
1769 "collection" => {
1770 if let Value::String(s) = arg.value {
1771 collection = s;
1772 }
1773 }
1774 "where" => filter = Some(value_to_filter(arg.value)?),
1775 _ => {}
1776 }
1777 }
1778 }
1779 Rule::sub_selection => {
1780 for sel in inner.into_inner() {
1781 if sel.as_rule() == Rule::selection_set {
1782 selection_set = parse_selection_set(sel)?;
1783 }
1784 }
1785 }
1786 _ => {}
1787 }
1788 }
1789
1790 Ok((MutationOp::Delete { collection, filter }, selection_set))
1791}
1792
1793fn parse_enqueue_job_mutation(
1794 pair: pest::iterators::Pair<Rule>,
1795) -> Result<(MutationOp, Vec<Selection>)> {
1796 let mut job_type = String::new();
1797 let mut payload = Value::Null;
1798 let mut priority = JobPriority::Normal;
1799 let mut scheduled_at = None;
1800 let mut max_retries = None;
1801 let mut selection_set = Vec::new();
1802
1803 for inner in pair.into_inner() {
1804 match inner.as_rule() {
1805 Rule::job_args => {
1806 for arg in parse_arguments_list(inner)? {
1807 match arg.name.as_str() {
1808 "jobType" => {
1809 if let Value::String(s) = arg.value {
1810 job_type = s;
1811 }
1812 }
1813 "payload" => payload = arg.value,
1814 "priority" => {
1815 if let Value::Enum(s) = arg.value {
1816 priority = match s.as_str() {
1817 "LOW" => JobPriority::Low,
1818 "HIGH" => JobPriority::High,
1819 "CRITICAL" => JobPriority::Critical,
1820 _ => JobPriority::Normal,
1821 };
1822 }
1823 }
1824 "scheduledAt" => {
1825 if let Value::String(s) = arg.value {
1826 scheduled_at = Some(s);
1827 }
1828 }
1829 "maxRetries" => {
1830 if let Value::Int(n) = arg.value {
1831 max_retries = Some(n as u32);
1832 }
1833 }
1834 _ => {}
1835 }
1836 }
1837 }
1838 Rule::sub_selection => {
1839 for sel in inner.into_inner() {
1840 if sel.as_rule() == Rule::selection_set {
1841 selection_set = parse_selection_set(sel)?;
1842 }
1843 }
1844 }
1845 _ => {}
1846 }
1847 }
1848
1849 Ok((
1850 MutationOp::EnqueueJob {
1851 job_type,
1852 payload,
1853 priority,
1854 scheduled_at,
1855 max_retries,
1856 },
1857 selection_set,
1858 ))
1859}
1860
1861fn parse_transaction_block(
1862 pair: pest::iterators::Pair<Rule>,
1863) -> Result<(MutationOp, Vec<Selection>)> {
1864 let mut operations = Vec::new();
1865 for inner in pair.into_inner() {
1866 if inner.as_rule() == Rule::mutation_set {
1867 operations = parse_mutation_set(inner)?;
1868 }
1869 }
1870 Ok((MutationOp::Transaction { operations }, Vec::new()))
1871}
1872
1873fn parse_arguments(pair: pest::iterators::Pair<Rule>) -> Result<Vec<Argument>> {
1874 let mut arguments = Vec::new();
1875 for inner in pair.into_inner() {
1876 match inner.as_rule() {
1877 Rule::argument_list => {
1878 for arg in inner.into_inner() {
1879 match arg.as_rule() {
1880 Rule::argument => arguments.push(parse_argument(arg)?),
1881 Rule::special_argument => {
1882 for special in arg.into_inner() {
1883 match special.as_rule() {
1884 Rule::search_args => {
1885 arguments.push(parse_search_args_to_argument(special)?);
1886 }
1887 Rule::validate_args => {
1888 arguments.push(parse_validate_args_to_argument(special)?);
1889 }
1890 Rule::order_by_clause => {
1891 arguments.push(parse_order_by_clause_to_argument(special)?);
1892 }
1893 _ => {}
1895 }
1896 }
1897 }
1898 _ => {}
1899 }
1900 }
1901 }
1902 Rule::argument => arguments.push(parse_argument(inner)?),
1903 _ => {}
1904 }
1905 }
1906 Ok(arguments)
1907}
1908
1909fn parse_search_args_to_argument(pair: pest::iterators::Pair<Rule>) -> Result<Argument> {
1910 let mut query = String::new();
1911 let mut fields: Vec<Value> = Vec::new();
1912 let mut fuzzy = Value::Boolean(false);
1913
1914 for inner in pair.into_inner() {
1915 if inner.as_rule() == Rule::search_options {
1916 for opt in inner.into_inner() {
1917 if opt.as_rule() == Rule::search_option {
1918 let s = opt.as_str();
1919 if s.starts_with("query") {
1920 for part in opt.into_inner() {
1921 if part.as_rule() == Rule::string {
1922 let raw = part.as_str();
1923 query = raw[1..raw.len() - 1].to_string();
1924 }
1925 }
1926 } else if s.starts_with("fields") {
1927 for part in opt.into_inner() {
1928 if part.as_rule() == Rule::array {
1929 if let Ok(Value::Array(arr)) = parse_value(part) {
1930 fields = arr;
1931 }
1932 }
1933 }
1934 } else if s.starts_with("fuzzy") {
1935 for part in opt.into_inner() {
1936 if part.as_rule() == Rule::boolean {
1937 fuzzy = Value::Boolean(part.as_str() == "true");
1938 }
1939 }
1940 }
1941 }
1942 }
1943 }
1944 }
1945
1946 let mut obj = std::collections::HashMap::new();
1947 obj.insert("query".to_string(), Value::String(query));
1948 obj.insert("fields".to_string(), Value::Array(fields));
1949 obj.insert("fuzzy".to_string(), fuzzy);
1950
1951 Ok(Argument {
1952 name: "search".to_string(),
1953 value: Value::Object(obj),
1954 })
1955}
1956
1957fn parse_validate_args_to_argument(pair: pest::iterators::Pair<Rule>) -> Result<Argument> {
1958 let mut rules_map = std::collections::HashMap::new();
1959
1960 for inner in pair.into_inner() {
1961 if inner.as_rule() == Rule::validation_rules {
1962 for rule in inner.into_inner() {
1963 if rule.as_rule() == Rule::validation_rule {
1964 let mut field_name = String::new();
1965 let mut constraints_map = std::collections::HashMap::new();
1966 for part in rule.into_inner() {
1967 match part.as_rule() {
1968 Rule::identifier | Rule::keyword => {
1969 field_name = part.as_str().to_string()
1970 }
1971 Rule::validation_constraints => {
1972 for constraint in part.into_inner() {
1973 if constraint.as_rule() == Rule::validation_constraint {
1974 let s = constraint.as_str();
1975 if s.starts_with("format") {
1976 for c in constraint.into_inner() {
1977 if c.as_rule() == Rule::string {
1978 let raw = c.as_str();
1979 constraints_map.insert(
1980 "format".to_string(),
1981 Value::String(
1982 raw[1..raw.len() - 1].to_string(),
1983 ),
1984 );
1985 }
1986 }
1987 } else if s.starts_with("minLength") {
1988 for c in constraint.into_inner() {
1989 if c.as_rule() == Rule::number {
1990 if let Ok(n) = c.as_str().parse::<i64>() {
1991 constraints_map.insert(
1992 "minLength".to_string(),
1993 Value::Int(n),
1994 );
1995 }
1996 }
1997 }
1998 } else if s.starts_with("maxLength") {
1999 for c in constraint.into_inner() {
2000 if c.as_rule() == Rule::number {
2001 if let Ok(n) = c.as_str().parse::<i64>() {
2002 constraints_map.insert(
2003 "maxLength".to_string(),
2004 Value::Int(n),
2005 );
2006 }
2007 }
2008 }
2009 } else if s.starts_with("min") {
2010 for c in constraint.into_inner() {
2011 if c.as_rule() == Rule::number {
2012 if let Ok(n) = c.as_str().parse::<f64>() {
2013 constraints_map.insert(
2014 "min".to_string(),
2015 Value::Float(n),
2016 );
2017 }
2018 }
2019 }
2020 } else if s.starts_with("max") {
2021 for c in constraint.into_inner() {
2022 if c.as_rule() == Rule::number {
2023 if let Ok(n) = c.as_str().parse::<f64>() {
2024 constraints_map.insert(
2025 "max".to_string(),
2026 Value::Float(n),
2027 );
2028 }
2029 }
2030 }
2031 } else if s.starts_with("pattern") {
2032 for c in constraint.into_inner() {
2033 if c.as_rule() == Rule::string {
2034 let raw = c.as_str();
2035 constraints_map.insert(
2036 "pattern".to_string(),
2037 Value::String(
2038 raw[1..raw.len() - 1].to_string(),
2039 ),
2040 );
2041 }
2042 }
2043 }
2044 }
2045 }
2046 }
2047 _ => {}
2048 }
2049 }
2050 if !field_name.is_empty() {
2051 rules_map.insert(field_name, Value::Object(constraints_map));
2052 }
2053 }
2054 }
2055 }
2056 }
2057
2058 Ok(Argument {
2059 name: "validate".to_string(),
2060 value: Value::Object(rules_map),
2061 })
2062}
2063
2064fn parse_order_by_clause_to_argument(pair: pest::iterators::Pair<Rule>) -> Result<Argument> {
2068 let mut orderings = std::collections::HashMap::new();
2069 for inner in pair.into_inner() {
2070 match inner.as_rule() {
2071 Rule::order_by_single => {
2072 let mut field = String::new();
2073 let mut direction = "ASC".to_string();
2074 for part in inner.into_inner() {
2075 match part.as_rule() {
2076 Rule::string => {
2077 let s = part.as_str();
2078 field = s[1..s.len() - 1].to_string();
2079 }
2080 Rule::sort_direction => {
2081 direction = part.as_str().to_string();
2082 }
2083 _ => {}
2084 }
2085 }
2086 if !field.is_empty() {
2087 orderings.insert(field, Value::Enum(direction));
2088 }
2089 }
2090 _ => {}
2091 }
2092 }
2093 Ok(Argument {
2094 name: "orderBy".to_string(),
2095 value: Value::Object(orderings),
2096 })
2097}
2098
2099fn parse_enqueue_jobs_mutation(
2100 pair: pest::iterators::Pair<Rule>,
2101) -> Result<(MutationOp, Vec<Selection>)> {
2102 let mut job_type = String::new();
2103 let mut payloads: Vec<Value> = Vec::new();
2104 let mut priority = JobPriority::Normal;
2105 let mut max_retries = None;
2106 let mut selection_set = Vec::new();
2107
2108 for inner in pair.into_inner() {
2109 match inner.as_rule() {
2110 Rule::jobs_args => {
2111 for arg in parse_arguments_list(inner)? {
2112 match arg.name.as_str() {
2113 "jobType" => {
2114 if let Value::String(s) = arg.value {
2115 job_type = s;
2116 }
2117 }
2118 "payloads" => {
2119 if let Value::Array(arr) = arg.value {
2120 payloads = arr;
2121 }
2122 }
2123 "priority" => {
2124 if let Value::Enum(s) = arg.value {
2125 priority = match s.as_str() {
2126 "LOW" => JobPriority::Low,
2127 "HIGH" => JobPriority::High,
2128 "CRITICAL" => JobPriority::Critical,
2129 _ => JobPriority::Normal,
2130 };
2131 }
2132 }
2133 "maxRetries" => {
2134 if let Value::Int(n) = arg.value {
2135 max_retries = Some(n as u32);
2136 }
2137 }
2138 _ => {}
2139 }
2140 }
2141 }
2142 Rule::sub_selection => {
2143 for sel in inner.into_inner() {
2144 if sel.as_rule() == Rule::selection_set {
2145 selection_set = parse_selection_set(sel)?;
2146 }
2147 }
2148 }
2149 _ => {}
2150 }
2151 }
2152
2153 Ok((
2154 MutationOp::EnqueueJobs {
2155 job_type,
2156 payloads,
2157 priority,
2158 max_retries,
2159 },
2160 selection_set,
2161 ))
2162}
2163
2164fn parse_import_mutation(
2165 pair: pest::iterators::Pair<Rule>,
2166) -> Result<(MutationOp, Vec<Selection>)> {
2167 let mut collection = String::new();
2168 let mut data: Vec<Value> = Vec::new();
2169 let mut selection_set = Vec::new();
2170
2171 for inner in pair.into_inner() {
2172 match inner.as_rule() {
2173 Rule::import_args => {
2174 for arg in parse_arguments_list(inner)? {
2175 match arg.name.as_str() {
2176 "collection" => {
2177 if let Value::String(s) = arg.value {
2178 collection = s;
2179 }
2180 }
2181 "data" => {
2182 if let Value::Array(arr) = arg.value {
2183 data = arr;
2184 }
2185 }
2186 _ => {}
2187 }
2188 }
2189 }
2190 Rule::sub_selection => {
2191 for sel in inner.into_inner() {
2192 if sel.as_rule() == Rule::selection_set {
2193 selection_set = parse_selection_set(sel)?;
2194 }
2195 }
2196 }
2197 _ => {}
2198 }
2199 }
2200
2201 Ok((MutationOp::Import { collection, data }, selection_set))
2202}
2203
2204fn parse_export_mutation(
2205 pair: pest::iterators::Pair<Rule>,
2206) -> Result<(MutationOp, Vec<Selection>)> {
2207 let mut collection = String::new();
2208 let mut format = "JSON".to_string();
2209 let mut selection_set = Vec::new();
2210
2211 for inner in pair.into_inner() {
2212 match inner.as_rule() {
2213 Rule::export_args => {
2214 for arg in parse_arguments_list(inner)? {
2215 match arg.name.as_str() {
2216 "collection" => {
2217 if let Value::String(s) = arg.value {
2218 collection = s;
2219 }
2220 }
2221 "format" => {
2222 let fmt = match arg.value {
2223 Value::Enum(s) | Value::String(s) => s,
2224 _ => "JSON".to_string(),
2225 };
2226 format = fmt;
2227 }
2228 _ => {}
2229 }
2230 }
2231 }
2232 Rule::sub_selection => {
2233 for sel in inner.into_inner() {
2234 if sel.as_rule() == Rule::selection_set {
2235 selection_set = parse_selection_set(sel)?;
2236 }
2237 }
2238 }
2239 _ => {}
2240 }
2241 }
2242
2243 Ok((MutationOp::Export { collection, format }, selection_set))
2244}
2245
2246fn parse_arguments_list(pair: pest::iterators::Pair<Rule>) -> Result<Vec<Argument>> {
2247 let mut arguments = Vec::new();
2248 for inner in pair.into_inner() {
2249 if inner.as_rule() == Rule::argument {
2250 arguments.push(parse_argument(inner)?);
2251 }
2252 }
2253 Ok(arguments)
2254}
2255
2256fn parse_argument(pair: pest::iterators::Pair<Rule>) -> Result<Argument> {
2257 let mut name = String::new();
2258 let mut value = Value::Null;
2259
2260 for inner in pair.into_inner() {
2261 match inner.as_rule() {
2262 Rule::identifier | Rule::keyword => name = inner.as_str().to_string(),
2263 Rule::value => value = parse_value(inner)?,
2264 _ => {}
2265 }
2266 }
2267
2268 Ok(Argument { name, value })
2269}
2270
2271fn parse_string(s: &str) -> String {
2275 if s.starts_with('"') && s.ends_with('"') {
2276 unescape_string(&s[1..s.len() - 1])
2277 } else {
2278 unescape_string(s)
2279 }
2280}
2281
2282fn unescape_string(s: &str) -> String {
2283 let mut result = String::with_capacity(s.len());
2284 let mut chars = s.chars().peekable();
2285 while let Some(c) = chars.next() {
2286 if c != '\\' {
2287 result.push(c);
2288 continue;
2289 }
2290 match chars.next() {
2291 Some('"') => result.push('"'),
2292 Some('\\') => result.push('\\'),
2293 Some('/') => result.push('/'),
2294 Some('n') => result.push('\n'),
2295 Some('r') => result.push('\r'),
2296 Some('t') => result.push('\t'),
2297 Some('b') => result.push('\x08'),
2298 Some('f') => result.push('\x0C'),
2299 Some('u') => {
2300 let hex: String = chars.by_ref().take(4).collect();
2301 if let Ok(n) = u32::from_str_radix(&hex, 16) {
2302 if let Some(uc) = char::from_u32(n) {
2303 result.push(uc);
2304 continue;
2305 }
2306 }
2307 result.push_str("\\u");
2308 result.push_str(&hex);
2309 }
2310 Some(other) => {
2311 result.push('\\');
2312 result.push(other);
2313 }
2314 None => result.push('\\'),
2315 }
2316 }
2317 result
2318}
2319
2320fn parse_value(pair: pest::iterators::Pair<Rule>) -> Result<Value> {
2321 match pair.as_rule() {
2322 Rule::value => {
2323 for inner in pair.into_inner() {
2324 return parse_value(inner);
2325 }
2326 Ok(Value::Null)
2327 }
2328 Rule::string => {
2329 let s = pair.as_str();
2330 let unquoted = if s.starts_with("\"\"\"") {
2331 &s[3..s.len() - 3]
2332 } else {
2333 &s[1..s.len() - 1]
2334 };
2335 Ok(Value::String(unescape_string(unquoted)))
2336 }
2337 Rule::number => {
2338 let s = pair.as_str();
2339 if s.contains('.') || s.contains('e') || s.contains('E') {
2340 Ok(Value::Float(s.parse().map_err(|e| {
2341 AqlError::new(ErrorCode::InvalidQuery, format!("Invalid float: {}", e))
2342 })?))
2343 } else {
2344 Ok(Value::Int(s.parse().map_err(|e| {
2345 AqlError::new(ErrorCode::InvalidQuery, format!("Invalid integer: {}", e))
2346 })?))
2347 }
2348 }
2349 Rule::boolean => Ok(Value::Boolean(pair.as_str() == "true")),
2350 Rule::null => Ok(Value::Null),
2351 Rule::variable => Ok(Value::Variable(
2352 pair.as_str().trim_start_matches('$').to_string(),
2353 )),
2354 Rule::enum_value => Ok(Value::Enum(pair.as_str().to_string())),
2355 Rule::array => {
2356 let mut values = Vec::new();
2357 for inner in pair.into_inner() {
2358 if inner.as_rule() == Rule::array_value_list {
2359 for val in inner.into_inner() {
2360 if val.as_rule() == Rule::value {
2361 values.push(parse_value(val)?);
2362 }
2363 }
2364 }
2365 }
2366 Ok(Value::Array(values))
2367 }
2368 Rule::object => {
2369 let mut map = HashMap::new();
2370 for inner in pair.into_inner() {
2371 if inner.as_rule() == Rule::object_field_list {
2372 for field in inner.into_inner() {
2373 if field.as_rule() == Rule::object_field {
2374 let (key, val) = parse_object_field(field)?;
2375 map.insert(key, val);
2376 }
2377 }
2378 }
2379 }
2380 Ok(Value::Object(map))
2381 }
2382 _ => Ok(Value::Null),
2383 }
2384}
2385fn parse_object_field(pair: pest::iterators::Pair<Rule>) -> Result<(String, Value)> {
2386 let mut key = String::new();
2387 let mut value = Value::Null;
2388
2389 for inner in pair.into_inner() {
2390 match inner.as_rule() {
2391 Rule::identifier | Rule::keyword => key = inner.as_str().to_string(),
2392 Rule::string => {
2393 let s = inner.as_str();
2394 key = s[1..s.len() - 1].to_string();
2395 }
2396 Rule::value => value = parse_value(inner)?,
2397 _ => {}
2398 }
2399 }
2400
2401 Ok((key, value))
2402}
2403
2404fn value_to_filter(value: Value) -> Result<Filter> {
2405 match value {
2406 Value::Object(map) => {
2407 let mut filters = Vec::new();
2408 for (key, val) in map {
2409 match key.as_str() {
2410 "and" => {
2411 if let Value::Array(arr) = val {
2412 let sub: Result<Vec<_>> =
2413 arr.into_iter().map(value_to_filter).collect();
2414 filters.push(Filter::And(sub?));
2415 }
2416 }
2417 "or" => {
2418 if let Value::Array(arr) = val {
2419 let sub: Result<Vec<_>> =
2420 arr.into_iter().map(value_to_filter).collect();
2421 filters.push(Filter::Or(sub?));
2422 }
2423 }
2424 "not" => filters.push(Filter::Not(Box::new(value_to_filter(val)?))),
2425 field => {
2426 if let Value::Object(ops) = val {
2427 for (op, op_val) in ops {
2428 let f = match op.as_str() {
2429 "eq" => Filter::Eq(field.to_string(), op_val),
2430 "ne" => Filter::Ne(field.to_string(), op_val),
2431 "gt" => Filter::Gt(field.to_string(), op_val),
2432 "gte" => Filter::Gte(field.to_string(), op_val),
2433 "lt" => Filter::Lt(field.to_string(), op_val),
2434 "lte" => Filter::Lte(field.to_string(), op_val),
2435 "in" => Filter::In(field.to_string(), op_val),
2436 "nin" => Filter::NotIn(field.to_string(), op_val),
2437 "contains" => Filter::Contains(field.to_string(), op_val),
2438 "containsAny" => Filter::ContainsAny(field.to_string(), op_val),
2439 "containsAll" => Filter::ContainsAll(field.to_string(), op_val),
2440 "startsWith" => Filter::StartsWith(field.to_string(), op_val),
2441 "endsWith" => Filter::EndsWith(field.to_string(), op_val),
2442 "isNull" => Filter::IsNull(field.to_string()),
2443 "isNotNull" => Filter::IsNotNull(field.to_string()),
2444 _ => {
2445 return Err(AqlError::new(
2446 ErrorCode::InvalidQuery,
2447 format!("Unknown filter operator: {}", op.as_str()),
2448 ));
2449 }
2450 };
2451 filters.push(f);
2452 }
2453 }
2454 }
2455 }
2456 }
2457 if filters.len() == 1 {
2458 Ok(filters.remove(0))
2459 } else {
2460 Ok(Filter::And(filters))
2461 }
2462 }
2463 _ => Err(AqlError::new(
2464 ErrorCode::InvalidQuery,
2465 "Filter must be an object".to_string(),
2466 )),
2467 }
2468}
2469
2470fn filter_to_value(filter: &Filter) -> Value {
2472 use std::collections::HashMap;
2473 match filter {
2474 Filter::Eq(field, val) => {
2475 let mut inner = HashMap::new();
2476 inner.insert("eq".to_string(), val.clone());
2477 let mut outer = HashMap::new();
2478 outer.insert(field.clone(), Value::Object(inner));
2479 Value::Object(outer)
2480 }
2481 Filter::Ne(field, val) => {
2482 let mut inner = HashMap::new();
2483 inner.insert("ne".to_string(), val.clone());
2484 let mut outer = HashMap::new();
2485 outer.insert(field.clone(), Value::Object(inner));
2486 Value::Object(outer)
2487 }
2488 Filter::Gt(field, val) => {
2489 let mut inner = HashMap::new();
2490 inner.insert("gt".to_string(), val.clone());
2491 let mut outer = HashMap::new();
2492 outer.insert(field.clone(), Value::Object(inner));
2493 Value::Object(outer)
2494 }
2495 Filter::Gte(field, val) => {
2496 let mut inner = HashMap::new();
2497 inner.insert("gte".to_string(), val.clone());
2498 let mut outer = HashMap::new();
2499 outer.insert(field.clone(), Value::Object(inner));
2500 Value::Object(outer)
2501 }
2502 Filter::Lt(field, val) => {
2503 let mut inner = HashMap::new();
2504 inner.insert("lt".to_string(), val.clone());
2505 let mut outer = HashMap::new();
2506 outer.insert(field.clone(), Value::Object(inner));
2507 Value::Object(outer)
2508 }
2509 Filter::Lte(field, val) => {
2510 let mut inner = HashMap::new();
2511 inner.insert("lte".to_string(), val.clone());
2512 let mut outer = HashMap::new();
2513 outer.insert(field.clone(), Value::Object(inner));
2514 Value::Object(outer)
2515 }
2516 Filter::In(field, val) => {
2517 let mut inner = HashMap::new();
2518 inner.insert("in".to_string(), val.clone());
2519 let mut outer = HashMap::new();
2520 outer.insert(field.clone(), Value::Object(inner));
2521 Value::Object(outer)
2522 }
2523 Filter::NotIn(field, val) => {
2524 let mut inner = HashMap::new();
2525 inner.insert("nin".to_string(), val.clone());
2526 let mut outer = HashMap::new();
2527 outer.insert(field.clone(), Value::Object(inner));
2528 Value::Object(outer)
2529 }
2530 Filter::Contains(field, val) => {
2531 let mut inner = HashMap::new();
2532 inner.insert("contains".to_string(), val.clone());
2533 let mut outer = HashMap::new();
2534 outer.insert(field.clone(), Value::Object(inner));
2535 Value::Object(outer)
2536 }
2537 Filter::ContainsAny(field, val) => {
2538 let mut inner = HashMap::new();
2539 inner.insert("containsAny".to_string(), val.clone());
2540 let mut outer = HashMap::new();
2541 outer.insert(field.clone(), Value::Object(inner));
2542 Value::Object(outer)
2543 }
2544 Filter::ContainsAll(field, val) => {
2545 let mut inner = HashMap::new();
2546 inner.insert("containsAll".to_string(), val.clone());
2547 let mut outer = HashMap::new();
2548 outer.insert(field.clone(), Value::Object(inner));
2549 Value::Object(outer)
2550 }
2551 Filter::StartsWith(field, val) => {
2552 let mut inner = HashMap::new();
2553 inner.insert("startsWith".to_string(), val.clone());
2554 let mut outer = HashMap::new();
2555 outer.insert(field.clone(), Value::Object(inner));
2556 Value::Object(outer)
2557 }
2558 Filter::EndsWith(field, val) => {
2559 let mut inner = HashMap::new();
2560 inner.insert("endsWith".to_string(), val.clone());
2561 let mut outer = HashMap::new();
2562 outer.insert(field.clone(), Value::Object(inner));
2563 Value::Object(outer)
2564 }
2565 Filter::Matches(field, val) => {
2566 let mut inner = HashMap::new();
2567 inner.insert("matches".to_string(), val.clone());
2568 let mut outer = HashMap::new();
2569 outer.insert(field.clone(), Value::Object(inner));
2570 Value::Object(outer)
2571 }
2572 Filter::IsNull(field) => {
2573 let mut inner = HashMap::new();
2574 inner.insert("isNull".to_string(), Value::Boolean(true));
2575 let mut outer = HashMap::new();
2576 outer.insert(field.clone(), Value::Object(inner));
2577 Value::Object(outer)
2578 }
2579 Filter::IsNotNull(field) => {
2580 let mut inner = HashMap::new();
2581 inner.insert("isNotNull".to_string(), Value::Boolean(true));
2582 let mut outer = HashMap::new();
2583 outer.insert(field.clone(), Value::Object(inner));
2584 Value::Object(outer)
2585 }
2586 Filter::And(filters) => {
2587 let arr: Vec<Value> = filters.iter().map(filter_to_value).collect();
2588 let mut map = HashMap::new();
2589 map.insert("and".to_string(), Value::Array(arr));
2590 Value::Object(map)
2591 }
2592 Filter::Or(filters) => {
2593 let arr: Vec<Value> = filters.iter().map(filter_to_value).collect();
2594 let mut map = HashMap::new();
2595 map.insert("or".to_string(), Value::Array(arr));
2596 Value::Object(map)
2597 }
2598 Filter::Not(inner) => {
2599 let mut map = HashMap::new();
2600 map.insert("not".to_string(), filter_to_value(inner));
2601 Value::Object(map)
2602 }
2603 }
2604}
2605
2606fn parse_fragment_definition(pair: pest::iterators::Pair<Rule>) -> Result<ast::FragmentDef> {
2608 let mut name = String::new();
2609 let mut type_condition = String::new();
2610 let mut selection_set = Vec::new();
2611
2612 for inner in pair.into_inner() {
2613 match inner.as_rule() {
2614 Rule::identifier | Rule::keyword => {
2615 if name.is_empty() {
2616 name = inner.as_str().to_string();
2617 } else {
2618 type_condition = inner.as_str().to_string();
2619 }
2620 }
2621 Rule::selection_set => {
2622 selection_set = parse_selection_set(inner)?;
2623 }
2624 _ => {}
2625 }
2626 }
2627
2628 Ok(ast::FragmentDef {
2629 name,
2630 type_condition,
2631 selection_set,
2632 })
2633}
2634
2635fn parse_introspection_query(pair: pest::iterators::Pair<Rule>) -> Result<ast::IntrospectionQuery> {
2636 let mut arguments = Vec::new();
2637 let mut fields = Vec::new();
2638
2639 for inner in pair.into_inner() {
2640 match inner.as_rule() {
2641 Rule::arguments => {
2642 arguments = parse_arguments(inner)?;
2643 }
2644 Rule::introspection_fields => {
2645 for field in inner.into_inner() {
2646 if field.as_rule() == Rule::introspection_field {
2647 fields.push(field.as_str().to_string());
2648 }
2649 }
2650 }
2651 _ => {}
2652 }
2653 }
2654
2655 Ok(ast::IntrospectionQuery { arguments, fields })
2656}
2657
2658fn parse_lookup_selection(pair: pest::iterators::Pair<Rule>) -> Result<ast::LookupSelection> {
2659 let mut collection = String::new();
2660 let mut local_field = String::new();
2661 let mut foreign_field = String::new();
2662 let mut filter = None;
2663 let mut selection_set = Vec::new();
2664
2665 for inner in pair.into_inner() {
2666 match inner.as_rule() {
2667 Rule::lookup_args => {
2668 for arg in inner.into_inner() {
2669 match arg.as_rule() {
2670 Rule::string => {
2671 let s = arg.as_str();
2672 let unquoted = &s[1..s.len() - 1];
2673 if collection.is_empty() {
2674 collection = unquoted.to_string();
2675 } else if local_field.is_empty() {
2676 local_field = unquoted.to_string();
2677 } else if foreign_field.is_empty() {
2678 foreign_field = unquoted.to_string();
2679 }
2680 }
2681 Rule::filter_object => {
2682 if let Ok(filter_value) = parse_filter_object_to_value(arg) {
2683 filter = Some(value_to_filter(filter_value)?);
2684 }
2685 }
2686 _ => {}
2687 }
2688 }
2689 }
2690 Rule::sub_selection => {
2691 for sel in inner.into_inner() {
2692 if sel.as_rule() == Rule::selection_set {
2693 let fields = parse_selection_set(sel)?;
2694 selection_set = fields;
2695 }
2696 }
2697 }
2698 _ => {}
2699 }
2700 }
2701
2702 if collection.is_empty() || local_field.is_empty() || foreign_field.is_empty() {
2703 return Err(AqlError::new(
2704 ErrorCode::InvalidQuery,
2705 "Lookup must specify collection, localField, and foreignField".to_string(),
2706 ));
2707 }
2708
2709 Ok(ast::LookupSelection {
2710 collection,
2711 local_field,
2712 foreign_field,
2713 filter,
2714 selection_set,
2715 })
2716}
2717
2718fn parse_handler_operation(pair: pest::iterators::Pair<Rule>) -> Result<ast::HandlerDef> {
2720 let mut name = String::new();
2721 let mut trigger = ast::HandlerTrigger::Custom(String::new());
2722 let mut action: Option<ast::MutationOperation> = None;
2723
2724 for inner in pair.into_inner() {
2725 match inner.as_rule() {
2726 Rule::string => {
2727 let s = inner.as_str();
2728 name = s[1..s.len() - 1].to_string();
2729 }
2730 Rule::handler_body => {
2731 for body in inner.into_inner() {
2732 match body.as_rule() {
2733 Rule::handler_trigger => {
2734 for t in body.into_inner() {
2736 if t.as_rule() == Rule::string {
2737 let raw = t.as_str();
2738 let s = &raw[1..raw.len() - 1];
2739 trigger = parse_trigger_string(s);
2740 }
2741 }
2742 }
2743 Rule::handler_action => {
2744 for a in body.into_inner() {
2746 if a.as_rule() == Rule::mutation_call {
2747 if let Ok((op, sel)) = parse_mutation_call(a) {
2748 action = Some(ast::MutationOperation {
2749 alias: None,
2750 operation: op,
2751 directives: vec![],
2752 selection_set: sel,
2753 });
2754 }
2755 }
2756 }
2757 }
2758 _ => {}
2759 }
2760 }
2761 }
2762 _ => {}
2763 }
2764 }
2765
2766 let action = action.ok_or_else(|| {
2767 AqlError::new(
2768 ErrorCode::InvalidQuery,
2769 "Handler must have an action".to_string(),
2770 )
2771 })?;
2772
2773 Ok(ast::HandlerDef {
2774 name,
2775 trigger,
2776 action,
2777 })
2778}
2779
2780fn parse_trigger_string(s: &str) -> ast::HandlerTrigger {
2781 let parts: Vec<&str> = s.splitn(2, ' ').collect();
2782 let event_type = parts[0].to_lowercase();
2783 let collection = parts.get(1).map(|c| c.to_string());
2784 match event_type.as_str() {
2785 "insert" => ast::HandlerTrigger::Insert { collection },
2786 "update" => ast::HandlerTrigger::Update { collection },
2787 "delete" => ast::HandlerTrigger::Delete { collection },
2788 "job_completed" | "jobcompleted" => ast::HandlerTrigger::JobCompleted,
2789 "job_failed" | "jobfailed" => ast::HandlerTrigger::JobFailed,
2790 _ => ast::HandlerTrigger::Custom(s.to_string()),
2791 }
2792}
2793
2794#[cfg(test)]
2795mod tests {
2796 use super::*;
2797
2798 #[test]
2799 fn test_parse_simple_query() {
2800 let query = r#"query { users { id name email } }"#;
2801 let result = parse(query);
2802 assert!(result.is_ok());
2803 let doc = result.unwrap();
2804 assert_eq!(doc.operations.len(), 1);
2805 }
2806
2807 #[test]
2808 fn test_parse_query_with_filter() {
2809 let query =
2810 r#"query GetActiveUsers { users(where: { active: { eq: true } }) { id name } }"#;
2811 let result = parse(query);
2812 assert!(result.is_ok());
2813 }
2814
2815 #[test]
2816 fn test_parse_mutation() {
2817 let query =
2818 r#"mutation { insertInto(collection: "users", data: { name: "John" }) { id } }"#;
2819 let result = parse(query);
2820 assert!(result.is_ok());
2821 }
2822
2823 #[test]
2824 fn test_parse_alter_collection() {
2825 let schema = r#"
2826 schema {
2827 alter collection users {
2828 add age: Int
2829 drop legacy_field
2830 rename name to full_name
2831 modify active: Boolean
2832 }
2833 }
2834 "#;
2835 let result = parse(schema);
2836 assert!(
2837 result.is_ok(),
2838 "Failed to parse alter collection: {:?}",
2839 result.err()
2840 );
2841
2842 let doc = result.unwrap();
2843 if let Operation::Schema(Schema { operations }) = &doc.operations[0] {
2844 if let SchemaOp::AlterCollection { name, actions } = &operations[0] {
2845 assert_eq!(name, "users");
2846 assert_eq!(actions.len(), 4);
2847
2848 match &actions[0] {
2850 AlterAction::AddField { field, .. } => {
2851 assert_eq!(field.name, "age");
2852 assert_eq!(field.field_type.name, "Int");
2853 }
2854 _ => panic!("Expected AddField"),
2855 }
2856
2857 match &actions[1] {
2859 AlterAction::DropField(name) => assert_eq!(name, "legacy_field"),
2860 _ => panic!("Expected DropField"),
2861 }
2862
2863 match &actions[2] {
2865 AlterAction::RenameField { from, to } => {
2866 assert_eq!(from, "name");
2867 assert_eq!(to, "full_name");
2868 }
2869 _ => panic!("Expected RenameField"),
2870 }
2871
2872 match &actions[3] {
2874 AlterAction::ModifyField(field) => {
2875 assert_eq!(field.name, "active");
2876 assert_eq!(field.field_type.name, "Boolean");
2877 }
2878 _ => panic!("Expected ModifyField"),
2879 }
2880 } else {
2881 panic!("Expected AlterCollection operation");
2882 }
2883 } else {
2884 panic!("Expected Schema operation");
2885 }
2886 }
2887
2888 #[test]
2889 fn test_parse_fragment_definition() {
2890 let query = r#"
2891 fragment UserFields on User {
2892 id
2893 name
2894 email
2895 }
2896 "#;
2897 let result = parse(query);
2898 assert!(
2899 result.is_ok(),
2900 "Failed to parse fragment: {:?}",
2901 result.err()
2902 );
2903 let doc = result.unwrap();
2904 assert_eq!(doc.operations.len(), 1);
2905
2906 if let Operation::FragmentDefinition(frag) = &doc.operations[0] {
2907 assert_eq!(frag.name, "UserFields");
2908 assert_eq!(frag.type_condition, "User");
2909 assert_eq!(frag.selection_set.len(), 3);
2910 } else {
2911 panic!("Expected FragmentDefinition");
2912 }
2913 }
2914
2915 #[test]
2916 fn test_parse_introspection() {
2917 let query = r#"__schema { collections, fields }"#;
2918 let result = parse(query);
2919 assert!(
2920 result.is_ok(),
2921 "Failed to parse introspection: {:?}",
2922 result.err()
2923 );
2924 let doc = result.unwrap();
2925 assert_eq!(doc.operations.len(), 1);
2926
2927 if let Operation::Introspection(intro) = &doc.operations[0] {
2928 assert_eq!(intro.fields.len(), 2);
2929 } else {
2930 panic!("Expected Introspection");
2931 }
2932 }
2933
2934 #[test]
2935 fn test_parse_fragment_with_query() {
2936 let query = r#"
2937 fragment UserFields on User {
2938 id
2939 name
2940 }
2941
2942 query GetUsers {
2943 users {
2944 ...UserFields
2945 }
2946 }
2947 "#;
2948 let result = parse(query);
2949 assert!(
2950 result.is_ok(),
2951 "Failed to parse fragment with query: {:?}",
2952 result.err()
2953 );
2954 let doc = result.unwrap();
2955 assert_eq!(doc.operations.len(), 2);
2956 }
2957
2958 #[test]
2959 fn test_parse_aggregate_with_alias() {
2960 let query = r#"
2961 query {
2962 products {
2963 stats: aggregate {
2964 totalStock: sum(field: "stock")
2965 avgPrice: avg(field: "price")
2966 count
2967 }
2968 }
2969 }
2970 "#;
2971 let result = parse(query);
2972 assert!(
2973 result.is_ok(),
2974 "Failed to parse aggregate: {:?}",
2975 result.err()
2976 );
2977
2978 let doc = result.unwrap();
2979 if let Operation::Query(q) = &doc.operations[0] {
2980 if let Selection::Field(products_field) = &q.selection_set[0] {
2981 assert_eq!(products_field.name, "products");
2982
2983 if let Selection::Field(agg_field) = &products_field.selection_set[0] {
2985 assert_eq!(agg_field.alias, Some("stats".to_string()));
2986 assert_eq!(agg_field.name, "aggregate");
2987
2988 assert_eq!(agg_field.selection_set.len(), 3, "Expected 3 agg functions");
2990
2991 if let Selection::Field(total_stock) = &agg_field.selection_set[0] {
2993 assert_eq!(
2994 total_stock.alias,
2995 Some("totalStock".to_string()),
2996 "Expected totalStock alias"
2997 );
2998 assert_eq!(total_stock.name, "sum");
2999 assert_eq!(total_stock.arguments.len(), 1);
3000 assert_eq!(total_stock.arguments[0].name, "field");
3001 } else {
3002 panic!("Expected Field for total_stock");
3003 }
3004
3005 if let Selection::Field(count) = &agg_field.selection_set[2] {
3007 assert_eq!(count.name, "count");
3008 } else {
3009 panic!("Expected Field for count");
3010 }
3011 } else {
3012 panic!("Expected Field for aggregate");
3013 }
3014 } else {
3015 panic!("Expected Field for products");
3016 }
3017 } else {
3018 panic!("Expected Query operation");
3019 }
3020 }
3021
3022 #[test]
3023 fn test_parse_lookup_selection() {
3024 let query = r#"
3025 query {
3026 orders {
3027 id
3028 total
3029 lookup(collection: "users", localField: "user_id", foreignField: "id") {
3030 name
3031 email
3032 }
3033 }
3034 }
3035 "#;
3036 let result = parse(query);
3037 assert!(result.is_ok(), "Failed to parse lookup: {:?}", result.err());
3038
3039 let doc = result.unwrap();
3040 if let Operation::Query(q) = &doc.operations[0] {
3041 if let Selection::Field(orders_field) = &q.selection_set[0] {
3042 assert_eq!(orders_field.name, "orders");
3043
3044 let lookup_selection = orders_field
3046 .selection_set
3047 .iter()
3048 .find(|f| match f {
3049 Selection::Field(field) => field.name == "lookup",
3050 _ => false,
3051 })
3052 .expect("Should have lookup field");
3053
3054 if let Selection::Field(lookup_field) = lookup_selection {
3055 assert!(
3057 lookup_field
3058 .arguments
3059 .iter()
3060 .any(|a| a.name == "collection"),
3061 "Should have collection argument"
3062 );
3063 assert!(
3064 lookup_field
3065 .arguments
3066 .iter()
3067 .any(|a| a.name == "localField"),
3068 "Should have localField argument"
3069 );
3070 assert!(
3071 lookup_field
3072 .arguments
3073 .iter()
3074 .any(|a| a.name == "foreignField"),
3075 "Should have foreignField argument"
3076 );
3077
3078 let collection_arg = lookup_field
3080 .arguments
3081 .iter()
3082 .find(|a| a.name == "collection")
3083 .unwrap();
3084 if let ast::Value::String(val) = &collection_arg.value {
3085 assert_eq!(val, "users");
3086 } else {
3087 panic!("collection should be a string");
3088 }
3089
3090 assert_eq!(lookup_field.selection_set.len(), 2);
3092 if let Selection::Field(f) = &lookup_field.selection_set[0] {
3093 assert_eq!(f.name, "name");
3094 }
3095 if let Selection::Field(f) = &lookup_field.selection_set[1] {
3096 assert_eq!(f.name, "email");
3097 }
3098 } else {
3099 panic!("Lookup should be a Field");
3100 }
3101 } else {
3102 panic!("Expected Field for orders");
3103 }
3104 } else {
3105 panic!("Expected Query operation");
3106 }
3107 }
3108
3109 #[test]
3110 fn test_parse_lookup_with_filter() {
3111 let query = r#"
3112 query {
3113 orders {
3114 id
3115 lookup(collection: "users", localField: "user_id", foreignField: "id", where: { active: { eq: true } }) {
3116 name
3117 }
3118 }
3119 }
3120 "#;
3121 let result = parse(query);
3122 assert!(
3123 result.is_ok(),
3124 "Failed to parse lookup with filter: {:?}",
3125 result.err()
3126 );
3127
3128 let doc = result.unwrap();
3129 if let Operation::Query(q) = &doc.operations[0] {
3130 if let Selection::Field(orders_field) = &q.selection_set[0] {
3131 let lookup_selection = orders_field
3132 .selection_set
3133 .iter()
3134 .find(|f| match f {
3135 Selection::Field(field) => field.name == "lookup",
3136 _ => false,
3137 })
3138 .expect("Should have lookup field");
3139
3140 if let Selection::Field(lookup_field) = lookup_selection {
3141 assert!(
3143 lookup_field.arguments.iter().any(|a| a.name == "where"),
3144 "Should have where argument for filter"
3145 );
3146 } else {
3147 panic!("Expected Field for lookup");
3148 }
3149 } else {
3150 panic!("Expected Field for orders");
3151 }
3152 } else {
3153 panic!("Expected Query operation");
3154 }
3155 }
3156}