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 => field_name = field_inner.as_str().to_string(),
577 Rule::string => {
578 let s = field_inner.as_str();
579 field_name = s[1..s.len() - 1].to_string();
580 }
581 Rule::filter_condition => {
582 parse_filter_condition_to_map(field_inner, &mut field_ops)?;
583 }
584 _ => {}
585 }
586 }
587
588 if !field_name.is_empty() {
589 map.insert(field_name, Value::Object(field_ops));
590 }
591 }
592 _ => {}
593 }
594 }
595 Ok(())
596}
597
598fn parse_logical_operator_to_map(
600 pair: pest::iterators::Pair<Rule>,
601 map: &mut HashMap<String, Value>,
602) -> Result<()> {
603 for inner in pair.into_inner() {
604 match inner.as_rule() {
605 Rule::and_operator => {
606 let mut filters = Vec::new();
607 for and_inner in inner.into_inner() {
608 if and_inner.as_rule() == Rule::filter_object {
609 filters.push(parse_filter_object_to_value(and_inner)?);
610 }
611 }
612 map.insert("and".to_string(), Value::Array(filters));
613 }
614 Rule::or_operator => {
615 let mut filters = Vec::new();
616 for or_inner in inner.into_inner() {
617 if or_inner.as_rule() == Rule::filter_object {
618 filters.push(parse_filter_object_to_value(or_inner)?);
619 }
620 }
621 map.insert("or".to_string(), Value::Array(filters));
622 }
623 Rule::not_operator => {
624 for not_inner in inner.into_inner() {
625 if not_inner.as_rule() == Rule::filter_object {
626 map.insert("not".to_string(), parse_filter_object_to_value(not_inner)?);
627 }
628 }
629 }
630 _ => {}
631 }
632 }
633 Ok(())
634}
635
636fn parse_filter_condition_to_map(
638 pair: pest::iterators::Pair<Rule>,
639 map: &mut HashMap<String, Value>,
640) -> Result<()> {
641 for inner in pair.into_inner() {
642 if inner.as_rule() == Rule::filter_operator_list {
643 for op in inner.into_inner() {
644 if op.as_rule() == Rule::filter_operator {
645 parse_filter_operator_to_map(op, map)?;
646 }
647 }
648 }
649 }
650 Ok(())
651}
652
653fn parse_filter_operator_to_map(
655 pair: pest::iterators::Pair<Rule>,
656 map: &mut HashMap<String, Value>,
657) -> Result<()> {
658 for inner in pair.into_inner() {
659 match inner.as_rule() {
660 Rule::eq_operator => {
661 for val in inner.into_inner() {
662 if val.as_rule() == Rule::value {
663 map.insert("eq".to_string(), parse_value(val)?);
664 }
665 }
666 }
667 Rule::ne_operator => {
668 for val in inner.into_inner() {
669 if val.as_rule() == Rule::value {
670 map.insert("ne".to_string(), parse_value(val)?);
671 }
672 }
673 }
674 Rule::gt_operator => {
675 for val in inner.into_inner() {
676 if val.as_rule() == Rule::value {
677 map.insert("gt".to_string(), parse_value(val)?);
678 }
679 }
680 }
681 Rule::gte_operator => {
682 for val in inner.into_inner() {
683 if val.as_rule() == Rule::value {
684 map.insert("gte".to_string(), parse_value(val)?);
685 }
686 }
687 }
688 Rule::lt_operator => {
689 for val in inner.into_inner() {
690 if val.as_rule() == Rule::value {
691 map.insert("lt".to_string(), parse_value(val)?);
692 }
693 }
694 }
695 Rule::lte_operator => {
696 for val in inner.into_inner() {
697 if val.as_rule() == Rule::value {
698 map.insert("lte".to_string(), parse_value(val)?);
699 }
700 }
701 }
702 Rule::in_operator => {
703 for val in inner.into_inner() {
704 if val.as_rule() == Rule::array {
705 map.insert("in".to_string(), parse_value(val)?);
706 }
707 }
708 }
709 Rule::nin_operator => {
710 for val in inner.into_inner() {
711 if val.as_rule() == Rule::array {
712 map.insert("nin".to_string(), parse_value(val)?);
713 }
714 }
715 }
716 Rule::contains_operator => {
717 for val in inner.into_inner() {
718 if val.as_rule() == Rule::value {
719 map.insert("contains".to_string(), parse_value(val)?);
720 }
721 }
722 }
723 Rule::starts_with_operator => {
724 for val in inner.into_inner() {
725 if val.as_rule() == Rule::value {
726 map.insert("startsWith".to_string(), parse_value(val)?);
727 }
728 }
729 }
730 Rule::ends_with_operator => {
731 for val in inner.into_inner() {
732 if val.as_rule() == Rule::value {
733 map.insert("endsWith".to_string(), parse_value(val)?);
734 }
735 }
736 }
737 Rule::contains_any_operator => {
738 for val in inner.into_inner() {
739 if val.as_rule() == Rule::array {
740 map.insert("containsAny".to_string(), parse_value(val)?);
741 }
742 }
743 }
744 Rule::contains_all_operator => {
745 for val in inner.into_inner() {
746 if val.as_rule() == Rule::array {
747 map.insert("containsAll".to_string(), parse_value(val)?);
748 }
749 }
750 }
751 Rule::matches_operator => {
752 for val in inner.into_inner() {
753 if val.as_rule() == Rule::value {
754 map.insert("matches".to_string(), parse_value(val)?);
755 }
756 }
757 }
758 Rule::increment_operator => {
759 for val in inner.into_inner() {
760 if val.as_rule() == Rule::number {
761 map.insert("increment".to_string(), parse_value(val)?);
762 }
763 }
764 }
765 Rule::decrement_operator => {
766 for val in inner.into_inner() {
767 if val.as_rule() == Rule::number {
768 map.insert("decrement".to_string(), parse_value(val)?);
769 }
770 }
771 }
772 Rule::push_operator => {
773 for val in inner.into_inner() {
774 if val.as_rule() == Rule::value {
775 map.insert("push".to_string(), parse_value(val)?);
776 }
777 }
778 }
779 Rule::pull_operator => {
780 for val in inner.into_inner() {
781 if val.as_rule() == Rule::value {
782 map.insert("pull".to_string(), parse_value(val)?);
783 }
784 }
785 }
786 Rule::add_to_set_operator => {
787 for val in inner.into_inner() {
788 if val.as_rule() == Rule::value {
789 map.insert("addToSet".to_string(), parse_value(val)?);
790 }
791 }
792 }
793 Rule::is_null_operator => {
794 map.insert("isNull".to_string(), Value::Boolean(true));
795 }
796 Rule::is_not_null_operator => {
797 map.insert("isNotNull".to_string(), Value::Boolean(true));
798 }
799 _ => {}
800 }
801 }
802 Ok(())
803}
804
805fn parse_variable_definitions(
806 pair: pest::iterators::Pair<Rule>,
807) -> Result<Vec<VariableDefinition>> {
808 let mut definitions = Vec::new();
809 for inner in pair.into_inner() {
810 if inner.as_rule() == Rule::variable_definition {
811 definitions.push(parse_variable_definition(inner)?);
812 }
813 }
814 Ok(definitions)
815}
816
817fn parse_variable_definition(pair: pest::iterators::Pair<Rule>) -> Result<VariableDefinition> {
818 let mut name = String::new();
819 let mut var_type = TypeAnnotation {
820 name: "String".to_string(),
821 is_array: false,
822 is_required: false,
823 };
824 let mut default_value = None;
825
826 for inner in pair.into_inner() {
827 match inner.as_rule() {
828 Rule::variable => name = inner.as_str().trim_start_matches('$').to_string(),
829 Rule::type_annotation => var_type = parse_type_annotation(inner)?,
830 Rule::default_value => {
831 for val in inner.into_inner() {
832 if val.as_rule() == Rule::value {
833 default_value = Some(parse_value(val)?);
834 }
835 }
836 }
837 _ => {}
838 }
839 }
840
841 Ok(VariableDefinition {
842 name,
843 var_type,
844 default_value,
845 })
846}
847
848fn parse_type_annotation(pair: pest::iterators::Pair<Rule>) -> Result<TypeAnnotation> {
849 let mut name = String::new();
850 let mut is_array = false;
851 let mut is_required = false;
852
853 for inner in pair.into_inner() {
854 match inner.as_rule() {
855 Rule::type_name => name = inner.as_str().to_string(),
856 Rule::array_type => {
857 is_array = true;
858 for arr_inner in inner.into_inner() {
859 if arr_inner.as_rule() == Rule::type_annotation {
860 let inner_type = parse_type_annotation(arr_inner)?;
861 name = inner_type.name;
862 }
863 }
864 }
865 Rule::type_modifier => is_required = true,
866 _ => name = inner.as_str().to_string(),
867 }
868 }
869
870 Ok(TypeAnnotation {
871 name,
872 is_array,
873 is_required,
874 })
875}
876
877fn parse_directives(pair: pest::iterators::Pair<Rule>) -> Result<Vec<Directive>> {
878 let mut directives = Vec::new();
879 for inner in pair.into_inner() {
880 if inner.as_rule() == Rule::directive {
881 directives.push(parse_directive(inner)?);
882 }
883 }
884 Ok(directives)
885}
886
887fn parse_directive(pair: pest::iterators::Pair<Rule>) -> Result<Directive> {
888 let mut name = String::new();
889 let mut arguments = Vec::new();
890
891 for inner in pair.into_inner() {
892 match inner.as_rule() {
893 Rule::directive_name | Rule::identifier | Rule::keyword => name = inner.as_str().to_string(),
894 Rule::arguments => arguments = parse_arguments(inner)?,
895 _ => {}
896 }
897 }
898
899 Ok(Directive { name, arguments })
900}
901
902fn parse_selection_set(pair: pest::iterators::Pair<Rule>) -> Result<Vec<Selection>> {
903 let mut selections = Vec::new();
904 for inner in pair.into_inner() {
905 if inner.as_rule() == Rule::field {
906 selections.push(parse_selection(inner)?);
907 }
908 }
909 Ok(selections)
910}
911
912fn parse_subscription_set(pair: pest::iterators::Pair<Rule>) -> Result<Vec<Selection>> {
913 let mut selections = Vec::new();
914 for inner in pair.into_inner() {
915 if inner.as_rule() == Rule::subscription_field {
916 selections.push(parse_selection(inner)?);
917 }
918 }
919 Ok(selections)
920}
921
922fn parse_selection(pair: pest::iterators::Pair<Rule>) -> Result<Selection> {
924 let inner = pair.clone().into_inner().next().unwrap();
929
930 match inner.as_rule() {
931 Rule::fragment_spread => {
932 let name = inner.into_inner().next().unwrap().as_str().to_string();
933 return Ok(Selection::FragmentSpread(name));
934 }
935 Rule::inline_fragment => {
936 let mut type_condition = String::new();
937 let mut selection_set = Vec::new();
938 for child in inner.into_inner() {
939 match child.as_rule() {
940 Rule::identifier | Rule::keyword => type_condition = child.as_str().to_string(),
941 Rule::selection_set => selection_set = parse_selection_set(child)?,
942 _ => {}
943 }
944 }
945 return Ok(Selection::InlineFragment(ast::InlineFragment {
946 type_condition,
947 selection_set,
948 }));
949 }
950 _ => {
951 return Ok(Selection::Field(parse_field_inner(pair)?));
953 }
954 }
955}
956
957fn parse_field_inner(pair: pest::iterators::Pair<Rule>) -> Result<Field> {
959 let mut alias = None;
960 let mut name = String::new();
961 let mut arguments = Vec::new();
962 let mut directives = Vec::new();
963 let mut selection_set = Vec::new();
964
965 for inner in pair.into_inner() {
966 match inner.as_rule() {
967 Rule::alias_name => {
968 for alias_inner in inner.into_inner() {
969 if alias_inner.as_rule() == Rule::identifier {
970 alias = Some(alias_inner.as_str().to_string());
971 }
972 }
973 }
974 Rule::identifier | Rule::keyword => name = inner.as_str().to_string(),
975 Rule::lookup_selection => {
976 name = "lookup".to_string();
977 for sel_inner in inner.clone().into_inner() {
979 if sel_inner.as_rule() == Rule::alias_name {
980 for alias_inner in sel_inner.into_inner() {
981 if alias_inner.as_rule() == Rule::identifier || alias_inner.as_rule() == Rule::keyword {
982 alias = Some(alias_inner.as_str().to_string());
983 }
984 }
985 }
986 }
987 let lookup = parse_lookup_selection(inner)?;
988 arguments.push(ast::Argument {
989 name: "collection".to_string(),
990 value: ast::Value::String(lookup.collection),
991 });
992 arguments.push(ast::Argument {
993 name: "localField".to_string(),
994 value: ast::Value::String(lookup.local_field),
995 });
996 arguments.push(ast::Argument {
997 name: "foreignField".to_string(),
998 value: ast::Value::String(lookup.foreign_field),
999 });
1000 if let Some(filter) = lookup.filter {
1001 arguments.push(ast::Argument {
1002 name: "where".to_string(),
1003 value: filter_to_value(&filter),
1004 });
1005 }
1006 selection_set = lookup.selection_set;
1007 }
1008 Rule::computed_field => {
1009 name = "__compute__".to_string();
1010 for cf_inner in inner.into_inner() {
1011 match cf_inner.as_rule() {
1012 Rule::identifier | Rule::keyword => alias = Some(cf_inner.as_str().to_string()),
1013 Rule::computed_expression => {
1014 let expr_str = cf_inner.as_str().trim();
1016 let val = if expr_str.starts_with('"') && expr_str.ends_with('"') {
1018 expr_str[1..expr_str.len()-1].to_string()
1019 } else {
1020 expr_str.to_string()
1021 };
1022 arguments.push(ast::Argument {
1023 name: "expr".to_string(),
1024 value: ast::Value::String(val),
1025 });
1026 }
1027 _ => {}
1028 }
1029 }
1030 }
1031 Rule::window_function => {
1032 name = "windowFunc".to_string();
1033 let mut parts = inner.into_inner();
1034 if let Some(alias_pair) = parts.next() {
1035 if alias_pair.as_rule() == Rule::identifier || alias_pair.as_rule() == Rule::keyword {
1036 alias = Some(alias_pair.as_str().to_string());
1037 }
1038 }
1039 for part in parts {
1040 if part.as_rule() == Rule::window_args {
1041 let mut strings: Vec<String> = Vec::new();
1042 let mut wsize: i64 = 3;
1043 for arg in part.into_inner() {
1044 match arg.as_rule() {
1045 Rule::string => {
1046 let s = arg.as_str();
1047 strings.push(parse_string(s));
1048 }
1049 Rule::number => {
1050 wsize = arg.as_str().parse().unwrap_or(3);
1051 }
1052 _ => {}
1053 }
1054 }
1055 if strings.len() >= 2 {
1056 arguments.push(ast::Argument { name: "field".to_string(), value: ast::Value::String(strings[0].clone()) });
1057 arguments.push(ast::Argument { name: "function".to_string(), value: ast::Value::String(strings[1].clone()) });
1058 }
1059 arguments.push(ast::Argument { name: "windowSize".to_string(), value: ast::Value::Int(wsize) });
1060 }
1061 }
1062 }
1063 Rule::arguments => arguments = parse_arguments(inner)?,
1064 Rule::directives => directives = parse_directives(inner)?,
1065 Rule::sub_selection => {
1066 for sel in inner.into_inner() {
1067 if sel.as_rule() == Rule::selection_set {
1068 selection_set = parse_selection_set(sel)?;
1069 }
1070 }
1071 }
1072 Rule::aggregate_with_alias => {
1073 name = "aggregate".to_string();
1074 for sel in inner.into_inner() {
1075 match sel.as_rule() {
1076 Rule::alias_name => {
1077 for alias_inner in sel.into_inner() {
1078 if alias_inner.as_rule() == Rule::identifier {
1079 alias = Some(alias_inner.as_str().to_string());
1080 }
1081 }
1082 }
1083 Rule::aggregate_field_list => {
1084 for agg_field in sel.into_inner() {
1085 if agg_field.as_rule() == Rule::aggregate_field {
1086 selection_set
1087 .push(Selection::Field(parse_aggregate_field(agg_field)?));
1088 }
1089 }
1090 }
1091 _ => {}
1092 }
1093 }
1094 }
1095 Rule::special_field_selection => {
1096 for sel in inner.into_inner() {
1097 match sel.as_rule() {
1098 Rule::alias_name => {
1099 for alias_inner in sel.into_inner() {
1100 if alias_inner.as_rule() == Rule::identifier {
1101 alias = Some(alias_inner.as_str().to_string());
1102 }
1103 }
1104 }
1105 Rule::group_by_selection => {
1106 name = "groupBy".to_string();
1107 for gb_inner in sel.into_inner() {
1108 match gb_inner.as_rule() {
1109 Rule::group_by_args => {
1110 for arg in gb_inner.into_inner() {
1111 match arg.as_rule() {
1112 Rule::string => {
1113 let s = arg.as_str();
1114 arguments.push(ast::Argument {
1115 name: "field".to_string(),
1116 value: ast::Value::String(parse_string(s)),
1117 });
1118 }
1119 Rule::array => {
1120 arguments.push(ast::Argument {
1121 name: "fields".to_string(),
1122 value: parse_value(arg)?,
1123 });
1124 }
1125 _ => {}
1126 }
1127 }
1128 }
1129 Rule::group_by_fields => {
1130 for field in gb_inner.into_inner() {
1131 if field.as_rule() == Rule::group_by_field {
1132 let mut alias = None;
1133 let mut name = String::new();
1134 let mut is_aggregate = false;
1135 let mut agg_field = None;
1136
1137 for f_inner in field.clone().into_inner() {
1138 match f_inner.as_rule() {
1139 Rule::alias_name => {
1140 for alias_inner in f_inner.into_inner() {
1141 if alias_inner.as_rule() == Rule::identifier || alias_inner.as_rule() == Rule::keyword {
1142 alias = Some(alias_inner.as_str().to_string());
1143 }
1144 }
1145 }
1146 Rule::identifier | Rule::keyword => {
1147 name = f_inner.as_str().to_string();
1148 }
1149 Rule::aggregate_with_alias => {
1150 is_aggregate = true;
1151 let agg_name = "aggregate".to_string();
1152 let mut agg_alias = None;
1153 let mut agg_selection_set = Vec::new();
1154 for sel in f_inner.into_inner() {
1155 match sel.as_rule() {
1156 Rule::alias_name => {
1157 for alias_inner in sel.into_inner() {
1158 if alias_inner.as_rule() == Rule::identifier {
1159 agg_alias = Some(alias_inner.as_str().to_string());
1160 }
1161 }
1162 }
1163 Rule::aggregate_field_list => {
1164 for agg_f in sel.into_inner() {
1165 if agg_f.as_rule() == Rule::aggregate_field {
1166 if let Ok(af) = parse_aggregate_field(agg_f) {
1167 agg_selection_set.push(Selection::Field(af));
1168 }
1169 }
1170 }
1171 }
1172 _ => {}
1173 }
1174 }
1175 agg_field = Some(Field {
1176 alias: agg_alias,
1177 name: agg_name,
1178 arguments: Vec::new(),
1179 directives: Vec::new(),
1180 selection_set: agg_selection_set,
1181 computed_expression: None,
1182 });
1183 }
1184 _ => {}
1185 }
1186 }
1187
1188 if is_aggregate {
1189 if let Some(f) = agg_field {
1190 selection_set.push(Selection::Field(f));
1191 }
1192 } else {
1193 if name.is_empty() {
1194 let raw = field.as_str().trim();
1195 let actual_name = if let Some(colon_idx) = raw.find(':') {
1196 raw[colon_idx + 1..].trim().to_string()
1197 } else {
1198 raw.to_string()
1199 };
1200 name = actual_name;
1201 }
1202 selection_set.push(Selection::Field(Field {
1203 alias,
1204 name,
1205 arguments: Vec::new(),
1206 directives: Vec::new(),
1207 selection_set: Vec::new(),
1208 computed_expression: None,
1209 }));
1210 }
1211 }
1212 }
1213 }
1214 _ => {}
1215 }
1216 }
1217 }
1218 Rule::lookup_selection => {
1219 name = "lookup".to_string();
1220 let lookup = parse_lookup_selection(sel)?;
1221 arguments.push(ast::Argument {
1222 name: "collection".to_string(),
1223 value: ast::Value::String(lookup.collection),
1224 });
1225 arguments.push(ast::Argument {
1226 name: "localField".to_string(),
1227 value: ast::Value::String(lookup.local_field),
1228 });
1229 arguments.push(ast::Argument {
1230 name: "foreignField".to_string(),
1231 value: ast::Value::String(lookup.foreign_field),
1232 });
1233 if let Some(filter) = lookup.filter {
1234 arguments.push(ast::Argument {
1235 name: "where".to_string(),
1236 value: filter_to_value(&filter),
1237 });
1238 }
1239 selection_set = lookup.selection_set;
1240 }
1241 Rule::page_info_selection => {
1242 name = "pageInfo".to_string();
1243 }
1244 Rule::edges_selection => {
1245 name = "edges".to_string();
1246 for edge_inner in sel.into_inner() {
1247 if edge_inner.as_rule() == Rule::edge_fields {
1248 for edge_field in edge_inner.into_inner() {
1249 if edge_field.as_rule() == Rule::edge_field {
1250 let edge_str = edge_field.as_str().trim();
1251 if edge_str.starts_with("cursor") {
1252 selection_set.push(Selection::Field(Field {
1253 alias: None,
1254 name: "cursor".to_string(),
1255 arguments: Vec::new(),
1256 directives: Vec::new(),
1257 selection_set: Vec::new(),
1258 computed_expression: None,
1259 }));
1260 } else if edge_str.starts_with("node") {
1261 let mut node_selection = Vec::new();
1262 for node_inner in edge_field.into_inner() {
1263 if node_inner.as_rule() == Rule::selection_set {
1264 node_selection =
1265 parse_selection_set(node_inner)?;
1266 }
1267 }
1268 selection_set.push(Selection::Field(Field {
1269 alias: None,
1270 name: "node".to_string(),
1271 arguments: Vec::new(),
1272 directives: Vec::new(),
1273 selection_set: node_selection,
1274 computed_expression: None,
1275 }));
1276 }
1277 }
1278 }
1279 }
1280 }
1281 }
1282 Rule::downsample_selection => {
1283 name = "downsample".to_string();
1284 for ds_inner in sel.into_inner() {
1285 match ds_inner.as_rule() {
1286 Rule::downsample_args => {
1287 let mut strings = Vec::new();
1288 for a in ds_inner.into_inner() {
1289 if a.as_rule() == Rule::string {
1290 let s = a.as_str();
1291 strings.push(s[1..s.len()-1].to_string());
1292 }
1293 }
1294 if strings.len() >= 2 {
1295 arguments.push(ast::Argument { name: "interval".to_string(), value: ast::Value::String(strings[0].clone()) });
1296 arguments.push(ast::Argument { name: "aggregation".to_string(), value: ast::Value::String(strings[1].clone()) });
1297 }
1298 }
1299 Rule::sub_selection => {
1300 for sub in ds_inner.into_inner() {
1301 if sub.as_rule() == Rule::selection_set {
1302 selection_set = parse_selection_set(sub)?;
1303 }
1304 }
1305 }
1306 _ => {}
1307 }
1308 }
1309 }
1310 Rule::window_function => {
1311 let mut parts = sel.into_inner();
1313 if let Some(alias_pair) = parts.next() {
1315 if alias_pair.as_rule() == Rule::identifier {
1316 alias = Some(alias_pair.as_str().to_string());
1317 }
1318 }
1319 name = "windowFunc".to_string();
1320 for part in parts {
1321 if part.as_rule() == Rule::window_args {
1322 let mut strings: Vec<String> = Vec::new();
1323 let mut wsize: i64 = 3;
1324 for arg in part.into_inner() {
1325 match arg.as_rule() {
1326 Rule::string => {
1327 let s = arg.as_str();
1328 strings.push(s[1..s.len()-1].to_string());
1329 }
1330 Rule::number => {
1331 wsize = arg.as_str().parse().unwrap_or(3);
1332 }
1333 _ => {}
1334 }
1335 }
1336 if strings.len() >= 2 {
1338 arguments.push(ast::Argument { name: "field".to_string(), value: ast::Value::String(strings[0].clone()) });
1339 arguments.push(ast::Argument { name: "function".to_string(), value: ast::Value::String(strings[1].clone()) });
1340 }
1341 arguments.push(ast::Argument { name: "windowSize".to_string(), value: ast::Value::Int(wsize) });
1342 }
1343 }
1344 }
1345 _ => {}
1346 }
1347 }
1348 }
1349 _ => {}
1350 }
1351 }
1352 Ok(Field {
1353 alias,
1354 name,
1355 arguments,
1356 directives,
1357 selection_set,
1358 computed_expression: None,
1359 })
1360}
1361
1362fn parse_aggregate_field(pair: pest::iterators::Pair<Rule>) -> Result<Field> {
1365 let mut name = String::new();
1366 let mut alias = None;
1367 let mut arguments = Vec::new();
1368 let pair_str = pair.as_str().to_string();
1369
1370 for inner in pair.into_inner() {
1371 match inner.as_rule() {
1372 Rule::aggregate_field_alias => {
1373 for alias_inner in inner.into_inner() {
1375 if alias_inner.as_rule() == Rule::identifier {
1376 alias = Some(alias_inner.as_str().to_string());
1377 }
1378 }
1379 }
1380 Rule::aggregate_field_value => {
1381 let inner_str = inner.as_str().to_string();
1383 for val_inner in inner.into_inner() {
1384 match val_inner.as_rule() {
1385 Rule::aggregate_function => {
1386 for fn_inner in val_inner.into_inner() {
1388 match fn_inner.as_rule() {
1389 Rule::aggregate_name => {
1390 name = fn_inner.as_str().to_string();
1391 }
1392 Rule::aggregate_args => {
1393 let mut arg_name = String::new();
1395 let mut arg_value = Value::Null;
1396
1397 for arg_inner in fn_inner.into_inner() {
1398 match arg_inner.as_rule() {
1399 Rule::string => {
1400 let s = arg_inner.as_str();
1401 arg_value = Value::String(
1402 s[1..s.len() - 1].to_string(),
1403 );
1404 if arg_name.is_empty() {
1405 arg_name = "field".to_string();
1406 }
1407 }
1408 Rule::array => {
1409 arg_value = parse_value(arg_inner)?;
1410 arg_name = "fields".to_string();
1411 }
1412 _ => {
1413 let text = arg_inner.as_str();
1415 if text == "field" || text == "fields" {
1416 arg_name = text.to_string();
1417 }
1418 }
1419 }
1420 }
1421
1422 if !arg_name.is_empty() && !matches!(arg_value, Value::Null)
1423 {
1424 arguments.push(Argument {
1425 name: arg_name,
1426 value: arg_value,
1427 });
1428 }
1429 }
1430 _ => {}
1431 }
1432 }
1433 }
1434 _ => {
1435 }
1437 }
1438 }
1439
1440 if name.is_empty() {
1442 let text = inner_str.trim();
1443 if text == "count" {
1444 name = "count".to_string();
1445 }
1446 }
1447 }
1448 _ => {}
1449 }
1450 }
1451
1452 if name.is_empty() {
1454 let text = pair_str.trim();
1455 if text == "count" || text.ends_with(": count") || text.contains("count") {
1456 name = "count".to_string();
1457 }
1458 }
1459 Ok(Field {
1460 alias,
1461 name,
1462 arguments,
1463 directives: vec![],
1464 selection_set: vec![],
1465 computed_expression: None,
1466 })
1467}
1468
1469fn parse_mutation_set(pair: pest::iterators::Pair<Rule>) -> Result<Vec<MutationOperation>> {
1470 let mut operations = Vec::new();
1471 for inner in pair.into_inner() {
1472 if inner.as_rule() == Rule::mutation_field {
1473 operations.push(parse_mutation_field(inner)?);
1474 }
1475 }
1476 Ok(operations)
1477}
1478
1479fn parse_mutation_field(pair: pest::iterators::Pair<Rule>) -> Result<MutationOperation> {
1480 let mut alias = None;
1481 let mut operation = MutationOp::Insert {
1482 collection: String::new(),
1483 data: Value::Null,
1484 };
1485 let mut directives = Vec::new();
1486 let mut selection_set = Vec::new();
1487
1488 for inner in pair.into_inner() {
1489 match inner.as_rule() {
1490 Rule::alias_name => {
1491 for alias_inner in inner.into_inner() {
1492 if alias_inner.as_rule() == Rule::identifier {
1493 alias = Some(alias_inner.as_str().to_string());
1494 }
1495 }
1496 }
1497 Rule::mutation_call => {
1498 let (op, sel) = parse_mutation_call(inner)?;
1499 operation = op;
1500 selection_set = sel;
1501 }
1502 Rule::directives => directives = parse_directives(inner)?,
1503 _ => {}
1504 }
1505 }
1506
1507 Ok(MutationOperation {
1508 alias,
1509 operation,
1510 directives,
1511 selection_set,
1512 })
1513}
1514
1515fn parse_mutation_call(pair: pest::iterators::Pair<Rule>) -> Result<(MutationOp, Vec<Selection>)> {
1516 for inner in pair.into_inner() {
1517 match inner.as_rule() {
1518 Rule::insert_mutation => return parse_insert_mutation(inner),
1519 Rule::insert_many_mutation => return parse_insert_many_mutation(inner),
1520 Rule::update_mutation => return parse_update_mutation(inner),
1521 Rule::upsert_mutation => return parse_upsert_mutation(inner),
1522 Rule::delete_mutation => return parse_delete_mutation(inner),
1523 Rule::enqueue_job_mutation => return parse_enqueue_job_mutation(inner),
1524 Rule::enqueue_jobs_mutation => return parse_enqueue_jobs_mutation(inner),
1525 Rule::import_mutation => return parse_import_mutation(inner),
1526 Rule::export_mutation => return parse_export_mutation(inner),
1527 Rule::transaction_block => return parse_transaction_block(inner),
1528 _ => {}
1529 }
1530 }
1531 Err(AqlError::new(
1532 ErrorCode::InvalidQuery,
1533 "Unknown mutation type".to_string(),
1534 ))
1535}
1536
1537fn parse_insert_mutation(
1538 pair: pest::iterators::Pair<Rule>,
1539) -> Result<(MutationOp, Vec<Selection>)> {
1540 let mut collection = String::new();
1541 let mut data = Value::Null;
1542 let mut selection_set = Vec::new();
1543
1544 for inner in pair.into_inner() {
1545 match inner.as_rule() {
1546 Rule::insert_args => {
1547 for arg in parse_arguments_list(inner)? {
1548 match arg.name.as_str() {
1549 "collection" => {
1550 if let Value::String(s) = arg.value {
1551 collection = s;
1552 }
1553 }
1554 "data" => data = arg.value,
1555 _ => {}
1556 }
1557 }
1558 }
1559 Rule::sub_selection => {
1560 for sel in inner.into_inner() {
1561 if sel.as_rule() == Rule::selection_set {
1562 selection_set = parse_selection_set(sel)?;
1563 }
1564 }
1565 }
1566 _ => {}
1567 }
1568 }
1569
1570 Ok((MutationOp::Insert { collection, data }, selection_set))
1571}
1572
1573fn parse_insert_many_mutation(
1574 pair: pest::iterators::Pair<Rule>,
1575) -> Result<(MutationOp, Vec<Selection>)> {
1576 let mut collection = String::new();
1577 let mut data = Value::Array(Vec::new());
1578 let mut selection_set = Vec::new();
1579
1580 for inner in pair.into_inner() {
1581 match inner.as_rule() {
1582 Rule::insert_many_args => {
1583 for arg in parse_arguments_list(inner)? {
1584 match arg.name.as_str() {
1585 "collection" => {
1586 if let Value::String(s) = arg.value {
1587 collection = s;
1588 }
1589 }
1590 "data" => {
1591 data = arg.value;
1592 }
1593 _ => {}
1594 }
1595 }
1596 }
1597 Rule::sub_selection => {
1598 for sel in inner.into_inner() {
1599 if sel.as_rule() == Rule::selection_set {
1600 selection_set = parse_selection_set(sel)?;
1601 }
1602 }
1603 }
1604 _ => {}
1605 }
1606 }
1607
1608 Ok((MutationOp::InsertMany { collection, data }, selection_set))
1609}
1610
1611fn parse_upsert_mutation(
1612 pair: pest::iterators::Pair<Rule>,
1613) -> Result<(MutationOp, Vec<Selection>)> {
1614 let mut collection = String::new();
1615 let mut filter = None;
1616 let mut data = Value::Null;
1617 let mut selection_set = Vec::new();
1618
1619 for inner in pair.into_inner() {
1620 match inner.as_rule() {
1621 Rule::upsert_args => {
1622 for arg in parse_arguments_list(inner)? {
1623 match arg.name.as_str() {
1624 "collection" => {
1625 if let Value::String(s) = arg.value {
1626 collection = s;
1627 }
1628 }
1629 "where" => filter = Some(value_to_filter(arg.value)?),
1630 "data" | "set" => data = arg.value,
1631 _ => {}
1632 }
1633 }
1634 }
1635 Rule::sub_selection => {
1636 for sel in inner.into_inner() {
1637 if sel.as_rule() == Rule::selection_set {
1638 selection_set = parse_selection_set(sel)?;
1639 }
1640 }
1641 }
1642 _ => {}
1643 }
1644 }
1645
1646 Ok((MutationOp::Upsert { collection, filter, data }, selection_set))
1647}
1648
1649fn parse_update_mutation(
1650 pair: pest::iterators::Pair<Rule>,
1651) -> Result<(MutationOp, Vec<Selection>)> {
1652 let mut collection = String::new();
1653 let mut filter = None;
1654 let mut data = Value::Null;
1655 let mut selection_set = Vec::new();
1656
1657 for inner in pair.into_inner() {
1658 match inner.as_rule() {
1659 Rule::update_args => {
1660 for arg in parse_arguments_list(inner)? {
1661 match arg.name.as_str() {
1662 "collection" => {
1663 if let Value::String(s) = arg.value {
1664 collection = s;
1665 }
1666 }
1667 "where" => filter = Some(value_to_filter(arg.value)?),
1668 "data" | "set" => data = arg.value,
1669 _ => {}
1670 }
1671 }
1672 }
1673 Rule::sub_selection => {
1674 for sel in inner.into_inner() {
1675 if sel.as_rule() == Rule::selection_set {
1676 selection_set = parse_selection_set(sel)?;
1677 }
1678 }
1679 }
1680 _ => {}
1681 }
1682 }
1683
1684 Ok((
1685 MutationOp::Update {
1686 collection,
1687 filter,
1688 data,
1689 },
1690 selection_set,
1691 ))
1692}
1693
1694fn parse_delete_mutation(
1695 pair: pest::iterators::Pair<Rule>,
1696) -> Result<(MutationOp, Vec<Selection>)> {
1697 let mut collection = String::new();
1698 let mut filter = None;
1699 let mut selection_set = Vec::new();
1700
1701 for inner in pair.into_inner() {
1702 match inner.as_rule() {
1703 Rule::delete_args => {
1704 for arg in parse_arguments_list(inner)? {
1705 match arg.name.as_str() {
1706 "collection" => {
1707 if let Value::String(s) = arg.value {
1708 collection = s;
1709 }
1710 }
1711 "where" => filter = Some(value_to_filter(arg.value)?),
1712 _ => {}
1713 }
1714 }
1715 }
1716 Rule::sub_selection => {
1717 for sel in inner.into_inner() {
1718 if sel.as_rule() == Rule::selection_set {
1719 selection_set = parse_selection_set(sel)?;
1720 }
1721 }
1722 }
1723 _ => {}
1724 }
1725 }
1726
1727 Ok((MutationOp::Delete { collection, filter }, selection_set))
1728}
1729
1730fn parse_enqueue_job_mutation(
1731 pair: pest::iterators::Pair<Rule>,
1732) -> Result<(MutationOp, Vec<Selection>)> {
1733 let mut job_type = String::new();
1734 let mut payload = Value::Null;
1735 let mut priority = JobPriority::Normal;
1736 let mut scheduled_at = None;
1737 let mut max_retries = None;
1738 let mut selection_set = Vec::new();
1739
1740 for inner in pair.into_inner() {
1741 match inner.as_rule() {
1742 Rule::job_args => {
1743 for arg in parse_arguments_list(inner)? {
1744 match arg.name.as_str() {
1745 "jobType" => {
1746 if let Value::String(s) = arg.value {
1747 job_type = s;
1748 }
1749 }
1750 "payload" => payload = arg.value,
1751 "priority" => {
1752 if let Value::Enum(s) = arg.value {
1753 priority = match s.as_str() {
1754 "LOW" => JobPriority::Low,
1755 "HIGH" => JobPriority::High,
1756 "CRITICAL" => JobPriority::Critical,
1757 _ => JobPriority::Normal,
1758 };
1759 }
1760 }
1761 "scheduledAt" => {
1762 if let Value::String(s) = arg.value {
1763 scheduled_at = Some(s);
1764 }
1765 }
1766 "maxRetries" => {
1767 if let Value::Int(n) = arg.value {
1768 max_retries = Some(n as u32);
1769 }
1770 }
1771 _ => {}
1772 }
1773 }
1774 }
1775 Rule::sub_selection => {
1776 for sel in inner.into_inner() {
1777 if sel.as_rule() == Rule::selection_set {
1778 selection_set = parse_selection_set(sel)?;
1779 }
1780 }
1781 }
1782 _ => {}
1783 }
1784 }
1785
1786 Ok((
1787 MutationOp::EnqueueJob {
1788 job_type,
1789 payload,
1790 priority,
1791 scheduled_at,
1792 max_retries,
1793 },
1794 selection_set,
1795 ))
1796}
1797
1798fn parse_transaction_block(
1799 pair: pest::iterators::Pair<Rule>,
1800) -> Result<(MutationOp, Vec<Selection>)> {
1801 let mut operations = Vec::new();
1802 for inner in pair.into_inner() {
1803 if inner.as_rule() == Rule::mutation_set {
1804 operations = parse_mutation_set(inner)?;
1805 }
1806 }
1807 Ok((MutationOp::Transaction { operations }, Vec::new()))
1808}
1809
1810fn parse_arguments(pair: pest::iterators::Pair<Rule>) -> Result<Vec<Argument>> {
1811 let mut arguments = Vec::new();
1812 for inner in pair.into_inner() {
1813 match inner.as_rule() {
1814 Rule::argument_list => {
1815 for arg in inner.into_inner() {
1816 match arg.as_rule() {
1817 Rule::argument => arguments.push(parse_argument(arg)?),
1818 Rule::special_argument => {
1819 for special in arg.into_inner() {
1820 match special.as_rule() {
1821 Rule::search_args => {
1822 arguments.push(parse_search_args_to_argument(special)?);
1823 }
1824 Rule::validate_args => {
1825 arguments.push(parse_validate_args_to_argument(special)?);
1826 }
1827 Rule::order_by_clause => {
1828 arguments.push(parse_order_by_clause_to_argument(special)?);
1829 }
1830 _ => {}
1832 }
1833 }
1834 }
1835 _ => {}
1836 }
1837 }
1838 }
1839 Rule::argument => arguments.push(parse_argument(inner)?),
1840 _ => {}
1841 }
1842 }
1843 Ok(arguments)
1844}
1845
1846fn parse_search_args_to_argument(pair: pest::iterators::Pair<Rule>) -> Result<Argument> {
1847 let mut query = String::new();
1848 let mut fields: Vec<Value> = Vec::new();
1849 let mut fuzzy = Value::Boolean(false);
1850
1851 for inner in pair.into_inner() {
1852 if inner.as_rule() == Rule::search_options {
1853 for opt in inner.into_inner() {
1854 if opt.as_rule() == Rule::search_option {
1855 let s = opt.as_str();
1856 if s.starts_with("query") {
1857 for part in opt.into_inner() {
1858 if part.as_rule() == Rule::string {
1859 let raw = part.as_str();
1860 query = raw[1..raw.len()-1].to_string();
1861 }
1862 }
1863 } else if s.starts_with("fields") {
1864 for part in opt.into_inner() {
1865 if part.as_rule() == Rule::array {
1866 if let Ok(Value::Array(arr)) = parse_value(part) {
1867 fields = arr;
1868 }
1869 }
1870 }
1871 } else if s.starts_with("fuzzy") {
1872 for part in opt.into_inner() {
1873 if part.as_rule() == Rule::boolean {
1874 fuzzy = Value::Boolean(part.as_str() == "true");
1875 }
1876 }
1877 }
1878 }
1879 }
1880 }
1881 }
1882
1883 let mut obj = std::collections::HashMap::new();
1884 obj.insert("query".to_string(), Value::String(query));
1885 obj.insert("fields".to_string(), Value::Array(fields));
1886 obj.insert("fuzzy".to_string(), fuzzy);
1887
1888 Ok(Argument { name: "search".to_string(), value: Value::Object(obj) })
1889}
1890
1891fn parse_validate_args_to_argument(pair: pest::iterators::Pair<Rule>) -> Result<Argument> {
1892 let mut rules_map = std::collections::HashMap::new();
1893
1894 for inner in pair.into_inner() {
1895 if inner.as_rule() == Rule::validation_rules {
1896 for rule in inner.into_inner() {
1897 if rule.as_rule() == Rule::validation_rule {
1898 let mut field_name = String::new();
1899 let mut constraints_map = std::collections::HashMap::new();
1900 for part in rule.into_inner() {
1901 match part.as_rule() {
1902 Rule::identifier | Rule::keyword => field_name = part.as_str().to_string(),
1903 Rule::validation_constraints => {
1904 for constraint in part.into_inner() {
1905 if constraint.as_rule() == Rule::validation_constraint {
1906 let s = constraint.as_str();
1907 if s.starts_with("format") {
1908 for c in constraint.into_inner() {
1909 if c.as_rule() == Rule::string {
1910 let raw = c.as_str();
1911 constraints_map.insert("format".to_string(), Value::String(raw[1..raw.len()-1].to_string()));
1912 }
1913 }
1914 } else if s.starts_with("minLength") {
1915 for c in constraint.into_inner() {
1916 if c.as_rule() == Rule::number {
1917 if let Ok(n) = c.as_str().parse::<i64>() {
1918 constraints_map.insert("minLength".to_string(), Value::Int(n));
1919 }
1920 }
1921 }
1922 } else if s.starts_with("maxLength") {
1923 for c in constraint.into_inner() {
1924 if c.as_rule() == Rule::number {
1925 if let Ok(n) = c.as_str().parse::<i64>() {
1926 constraints_map.insert("maxLength".to_string(), Value::Int(n));
1927 }
1928 }
1929 }
1930 } else if s.starts_with("min") {
1931 for c in constraint.into_inner() {
1932 if c.as_rule() == Rule::number {
1933 if let Ok(n) = c.as_str().parse::<f64>() {
1934 constraints_map.insert("min".to_string(), Value::Float(n));
1935 }
1936 }
1937 }
1938 } else if s.starts_with("max") {
1939 for c in constraint.into_inner() {
1940 if c.as_rule() == Rule::number {
1941 if let Ok(n) = c.as_str().parse::<f64>() {
1942 constraints_map.insert("max".to_string(), Value::Float(n));
1943 }
1944 }
1945 }
1946 } else if s.starts_with("pattern") {
1947 for c in constraint.into_inner() {
1948 if c.as_rule() == Rule::string {
1949 let raw = c.as_str();
1950 constraints_map.insert("pattern".to_string(), Value::String(raw[1..raw.len()-1].to_string()));
1951 }
1952 }
1953 }
1954 }
1955 }
1956 }
1957 _ => {}
1958 }
1959 }
1960 if !field_name.is_empty() {
1961 rules_map.insert(field_name, Value::Object(constraints_map));
1962 }
1963 }
1964 }
1965 }
1966 }
1967
1968 Ok(Argument { name: "validate".to_string(), value: Value::Object(rules_map) })
1969}
1970
1971fn parse_order_by_clause_to_argument(pair: pest::iterators::Pair<Rule>) -> Result<Argument> {
1975 let mut orderings = std::collections::HashMap::new();
1976 for inner in pair.into_inner() {
1977 match inner.as_rule() {
1978 Rule::order_by_single => {
1979 let mut field = String::new();
1980 let mut direction = "ASC".to_string();
1981 for part in inner.into_inner() {
1982 match part.as_rule() {
1983 Rule::string => {
1984 let s = part.as_str();
1985 field = s[1..s.len() - 1].to_string();
1986 }
1987 Rule::sort_direction => {
1988 direction = part.as_str().to_string();
1989 }
1990 _ => {}
1991 }
1992 }
1993 if !field.is_empty() {
1994 orderings.insert(field, Value::Enum(direction));
1995 }
1996 }
1997 _ => {}
1998 }
1999 }
2000 Ok(Argument {
2001 name: "orderBy".to_string(),
2002 value: Value::Object(orderings),
2003 })
2004}
2005
2006fn parse_enqueue_jobs_mutation(
2007 pair: pest::iterators::Pair<Rule>,
2008) -> Result<(MutationOp, Vec<Selection>)> {
2009 let mut job_type = String::new();
2010 let mut payloads: Vec<Value> = Vec::new();
2011 let mut priority = JobPriority::Normal;
2012 let mut max_retries = None;
2013 let mut selection_set = Vec::new();
2014
2015 for inner in pair.into_inner() {
2016 match inner.as_rule() {
2017 Rule::jobs_args => {
2018 for arg in parse_arguments_list(inner)? {
2019 match arg.name.as_str() {
2020 "jobType" => {
2021 if let Value::String(s) = arg.value {
2022 job_type = s;
2023 }
2024 }
2025 "payloads" => {
2026 if let Value::Array(arr) = arg.value {
2027 payloads = arr;
2028 }
2029 }
2030 "priority" => {
2031 if let Value::Enum(s) = arg.value {
2032 priority = match s.as_str() {
2033 "LOW" => JobPriority::Low,
2034 "HIGH" => JobPriority::High,
2035 "CRITICAL" => JobPriority::Critical,
2036 _ => JobPriority::Normal,
2037 };
2038 }
2039 }
2040 "maxRetries" => {
2041 if let Value::Int(n) = arg.value {
2042 max_retries = Some(n as u32);
2043 }
2044 }
2045 _ => {}
2046 }
2047 }
2048 }
2049 Rule::sub_selection => {
2050 for sel in inner.into_inner() {
2051 if sel.as_rule() == Rule::selection_set {
2052 selection_set = parse_selection_set(sel)?;
2053 }
2054 }
2055 }
2056 _ => {}
2057 }
2058 }
2059
2060 Ok((
2061 MutationOp::EnqueueJobs { job_type, payloads, priority, max_retries },
2062 selection_set,
2063 ))
2064}
2065
2066fn parse_import_mutation(
2067 pair: pest::iterators::Pair<Rule>,
2068) -> Result<(MutationOp, Vec<Selection>)> {
2069 let mut collection = String::new();
2070 let mut data: Vec<Value> = Vec::new();
2071 let mut selection_set = Vec::new();
2072
2073 for inner in pair.into_inner() {
2074 match inner.as_rule() {
2075 Rule::import_args => {
2076 for arg in parse_arguments_list(inner)? {
2077 match arg.name.as_str() {
2078 "collection" => {
2079 if let Value::String(s) = arg.value {
2080 collection = s;
2081 }
2082 }
2083 "data" => {
2084 if let Value::Array(arr) = arg.value {
2085 data = arr;
2086 }
2087 }
2088 _ => {}
2089 }
2090 }
2091 }
2092 Rule::sub_selection => {
2093 for sel in inner.into_inner() {
2094 if sel.as_rule() == Rule::selection_set {
2095 selection_set = parse_selection_set(sel)?;
2096 }
2097 }
2098 }
2099 _ => {}
2100 }
2101 }
2102
2103 Ok((MutationOp::Import { collection, data }, selection_set))
2104}
2105
2106fn parse_export_mutation(
2107 pair: pest::iterators::Pair<Rule>,
2108) -> Result<(MutationOp, Vec<Selection>)> {
2109 let mut collection = String::new();
2110 let mut format = "JSON".to_string();
2111 let mut selection_set = Vec::new();
2112
2113 for inner in pair.into_inner() {
2114 match inner.as_rule() {
2115 Rule::export_args => {
2116 for arg in parse_arguments_list(inner)? {
2117 match arg.name.as_str() {
2118 "collection" => {
2119 if let Value::String(s) = arg.value {
2120 collection = s;
2121 }
2122 }
2123 "format" => {
2124 let fmt = match arg.value {
2125 Value::Enum(s) | Value::String(s) => s,
2126 _ => "JSON".to_string(),
2127 };
2128 format = fmt;
2129 }
2130 _ => {}
2131 }
2132 }
2133 }
2134 Rule::sub_selection => {
2135 for sel in inner.into_inner() {
2136 if sel.as_rule() == Rule::selection_set {
2137 selection_set = parse_selection_set(sel)?;
2138 }
2139 }
2140 }
2141 _ => {}
2142 }
2143 }
2144
2145 Ok((MutationOp::Export { collection, format }, selection_set))
2146}
2147
2148fn parse_arguments_list(pair: pest::iterators::Pair<Rule>) -> Result<Vec<Argument>> {
2149 let mut arguments = Vec::new();
2150 for inner in pair.into_inner() {
2151 if inner.as_rule() == Rule::argument {
2152 arguments.push(parse_argument(inner)?);
2153 }
2154 }
2155 Ok(arguments)
2156}
2157
2158fn parse_argument(pair: pest::iterators::Pair<Rule>) -> Result<Argument> {
2159 let mut name = String::new();
2160 let mut value = Value::Null;
2161
2162 for inner in pair.into_inner() {
2163 match inner.as_rule() {
2164 Rule::identifier | Rule::keyword => name = inner.as_str().to_string(),
2165 Rule::value => value = parse_value(inner)?,
2166 _ => {}
2167 }
2168 }
2169
2170 Ok(Argument { name, value })
2171}
2172
2173fn parse_string(s: &str) -> String {
2177 if s.starts_with('"') && s.ends_with('"') {
2178 unescape_string(&s[1..s.len() - 1])
2179 } else {
2180 unescape_string(s)
2181 }
2182}
2183
2184fn unescape_string(s: &str) -> String {
2185 let mut result = String::with_capacity(s.len());
2186 let mut chars = s.chars().peekable();
2187 while let Some(c) = chars.next() {
2188 if c != '\\' {
2189 result.push(c);
2190 continue;
2191 }
2192 match chars.next() {
2193 Some('"') => result.push('"'),
2194 Some('\\') => result.push('\\'),
2195 Some('/') => result.push('/'),
2196 Some('n') => result.push('\n'),
2197 Some('r') => result.push('\r'),
2198 Some('t') => result.push('\t'),
2199 Some('b') => result.push('\x08'),
2200 Some('f') => result.push('\x0C'),
2201 Some('u') => {
2202 let hex: String = chars.by_ref().take(4).collect();
2203 if let Ok(n) = u32::from_str_radix(&hex, 16) {
2204 if let Some(uc) = char::from_u32(n) {
2205 result.push(uc);
2206 continue;
2207 }
2208 }
2209 result.push_str("\\u");
2210 result.push_str(&hex);
2211 }
2212 Some(other) => {
2213 result.push('\\');
2214 result.push(other);
2215 }
2216 None => result.push('\\'),
2217 }
2218 }
2219 result
2220}
2221
2222fn parse_value(pair: pest::iterators::Pair<Rule>) -> Result<Value> {
2223 match pair.as_rule() {
2224 Rule::value => {
2225 for inner in pair.into_inner() {
2226 return parse_value(inner);
2227 }
2228 Ok(Value::Null)
2229 }
2230 Rule::string => {
2231 let s = pair.as_str();
2232 let unquoted = if s.starts_with("\"\"\"") {
2233 &s[3..s.len() - 3]
2234 } else {
2235 &s[1..s.len() - 1]
2236 };
2237 Ok(Value::String(unescape_string(unquoted)))
2238 }
2239 Rule::number => {
2240 let s = pair.as_str();
2241 if s.contains('.') || s.contains('e') || s.contains('E') {
2242 Ok(Value::Float(s.parse().map_err(|e| {
2243 AqlError::new(ErrorCode::InvalidQuery, format!("Invalid float: {}", e))
2244 })?))
2245 } else {
2246 Ok(Value::Int(s.parse().map_err(|e| {
2247 AqlError::new(ErrorCode::InvalidQuery, format!("Invalid integer: {}", e))
2248 })?))
2249 }
2250 }
2251 Rule::boolean => Ok(Value::Boolean(pair.as_str() == "true")),
2252 Rule::null => Ok(Value::Null),
2253 Rule::variable => Ok(Value::Variable(
2254 pair.as_str().trim_start_matches('$').to_string(),
2255 )),
2256 Rule::enum_value => Ok(Value::Enum(pair.as_str().to_string())),
2257 Rule::array => {
2258 let mut values = Vec::new();
2259 for inner in pair.into_inner() {
2260 if inner.as_rule() == Rule::array_value_list {
2261 for val in inner.into_inner() {
2262 if val.as_rule() == Rule::value {
2263 values.push(parse_value(val)?);
2264 }
2265 }
2266 }
2267 }
2268 Ok(Value::Array(values))
2269 }
2270 Rule::object => {
2271 let mut map = HashMap::new();
2272 for inner in pair.into_inner() {
2273 if inner.as_rule() == Rule::object_field_list {
2274 for field in inner.into_inner() {
2275 if field.as_rule() == Rule::object_field {
2276 let (key, val) = parse_object_field(field)?;
2277 map.insert(key, val);
2278 }
2279 }
2280 }
2281 }
2282 Ok(Value::Object(map))
2283 }
2284 _ => Ok(Value::Null),
2285 }
2286}
2287fn parse_object_field(pair: pest::iterators::Pair<Rule>) -> Result<(String, Value)> {
2288 let mut key = String::new();
2289 let mut value = Value::Null;
2290
2291 for inner in pair.into_inner() {
2292 match inner.as_rule() {
2293 Rule::identifier | Rule::keyword => key = inner.as_str().to_string(),
2294 Rule::string => {
2295 let s = inner.as_str();
2296 key = s[1..s.len() - 1].to_string();
2297 }
2298 Rule::value => value = parse_value(inner)?,
2299 _ => {}
2300 }
2301 }
2302
2303 Ok((key, value))
2304}
2305
2306fn value_to_filter(value: Value) -> Result<Filter> {
2307 match value {
2308 Value::Object(map) => {
2309 let mut filters = Vec::new();
2310 for (key, val) in map {
2311 match key.as_str() {
2312 "and" => {
2313 if let Value::Array(arr) = val {
2314 let sub: Result<Vec<_>> =
2315 arr.into_iter().map(value_to_filter).collect();
2316 filters.push(Filter::And(sub?));
2317 }
2318 }
2319 "or" => {
2320 if let Value::Array(arr) = val {
2321 let sub: Result<Vec<_>> =
2322 arr.into_iter().map(value_to_filter).collect();
2323 filters.push(Filter::Or(sub?));
2324 }
2325 }
2326 "not" => filters.push(Filter::Not(Box::new(value_to_filter(val)?))),
2327 field => {
2328 if let Value::Object(ops) = val {
2329 for (op, op_val) in ops {
2330 let f = match op.as_str() {
2331 "eq" => Filter::Eq(field.to_string(), op_val),
2332 "ne" => Filter::Ne(field.to_string(), op_val),
2333 "gt" => Filter::Gt(field.to_string(), op_val),
2334 "gte" => Filter::Gte(field.to_string(), op_val),
2335 "lt" => Filter::Lt(field.to_string(), op_val),
2336 "lte" => Filter::Lte(field.to_string(), op_val),
2337 "in" => Filter::In(field.to_string(), op_val),
2338 "nin" => Filter::NotIn(field.to_string(), op_val),
2339 "contains" => Filter::Contains(field.to_string(), op_val),
2340 "containsAny" => Filter::ContainsAny(field.to_string(), op_val),
2341 "containsAll" => Filter::ContainsAll(field.to_string(), op_val),
2342 "startsWith" => Filter::StartsWith(field.to_string(), op_val),
2343 "endsWith" => Filter::EndsWith(field.to_string(), op_val),
2344 "isNull" => Filter::IsNull(field.to_string()),
2345 "isNotNull" => Filter::IsNotNull(field.to_string()),
2346 _ => {
2347 return Err(AqlError::new(
2348 ErrorCode::InvalidQuery,
2349 format!("Unknown filter operator: {}", op.as_str()),
2350 ));
2351 }
2352 };
2353 filters.push(f);
2354 }
2355 }
2356 }
2357 }
2358 }
2359 if filters.len() == 1 {
2360 Ok(filters.remove(0))
2361 } else {
2362 Ok(Filter::And(filters))
2363 }
2364 }
2365 _ => Err(AqlError::new(
2366 ErrorCode::InvalidQuery,
2367 "Filter must be an object".to_string(),
2368 )),
2369 }
2370}
2371
2372fn filter_to_value(filter: &Filter) -> Value {
2374 use std::collections::HashMap;
2375 match filter {
2376 Filter::Eq(field, val) => {
2377 let mut inner = HashMap::new();
2378 inner.insert("eq".to_string(), val.clone());
2379 let mut outer = HashMap::new();
2380 outer.insert(field.clone(), Value::Object(inner));
2381 Value::Object(outer)
2382 }
2383 Filter::Ne(field, val) => {
2384 let mut inner = HashMap::new();
2385 inner.insert("ne".to_string(), val.clone());
2386 let mut outer = HashMap::new();
2387 outer.insert(field.clone(), Value::Object(inner));
2388 Value::Object(outer)
2389 }
2390 Filter::Gt(field, val) => {
2391 let mut inner = HashMap::new();
2392 inner.insert("gt".to_string(), val.clone());
2393 let mut outer = HashMap::new();
2394 outer.insert(field.clone(), Value::Object(inner));
2395 Value::Object(outer)
2396 }
2397 Filter::Gte(field, val) => {
2398 let mut inner = HashMap::new();
2399 inner.insert("gte".to_string(), val.clone());
2400 let mut outer = HashMap::new();
2401 outer.insert(field.clone(), Value::Object(inner));
2402 Value::Object(outer)
2403 }
2404 Filter::Lt(field, val) => {
2405 let mut inner = HashMap::new();
2406 inner.insert("lt".to_string(), val.clone());
2407 let mut outer = HashMap::new();
2408 outer.insert(field.clone(), Value::Object(inner));
2409 Value::Object(outer)
2410 }
2411 Filter::Lte(field, val) => {
2412 let mut inner = HashMap::new();
2413 inner.insert("lte".to_string(), val.clone());
2414 let mut outer = HashMap::new();
2415 outer.insert(field.clone(), Value::Object(inner));
2416 Value::Object(outer)
2417 }
2418 Filter::In(field, val) => {
2419 let mut inner = HashMap::new();
2420 inner.insert("in".to_string(), val.clone());
2421 let mut outer = HashMap::new();
2422 outer.insert(field.clone(), Value::Object(inner));
2423 Value::Object(outer)
2424 }
2425 Filter::NotIn(field, val) => {
2426 let mut inner = HashMap::new();
2427 inner.insert("nin".to_string(), val.clone());
2428 let mut outer = HashMap::new();
2429 outer.insert(field.clone(), Value::Object(inner));
2430 Value::Object(outer)
2431 }
2432 Filter::Contains(field, val) => {
2433 let mut inner = HashMap::new();
2434 inner.insert("contains".to_string(), val.clone());
2435 let mut outer = HashMap::new();
2436 outer.insert(field.clone(), Value::Object(inner));
2437 Value::Object(outer)
2438 }
2439 Filter::ContainsAny(field, val) => {
2440 let mut inner = HashMap::new();
2441 inner.insert("containsAny".to_string(), val.clone());
2442 let mut outer = HashMap::new();
2443 outer.insert(field.clone(), Value::Object(inner));
2444 Value::Object(outer)
2445 }
2446 Filter::ContainsAll(field, val) => {
2447 let mut inner = HashMap::new();
2448 inner.insert("containsAll".to_string(), val.clone());
2449 let mut outer = HashMap::new();
2450 outer.insert(field.clone(), Value::Object(inner));
2451 Value::Object(outer)
2452 }
2453 Filter::StartsWith(field, val) => {
2454 let mut inner = HashMap::new();
2455 inner.insert("startsWith".to_string(), val.clone());
2456 let mut outer = HashMap::new();
2457 outer.insert(field.clone(), Value::Object(inner));
2458 Value::Object(outer)
2459 }
2460 Filter::EndsWith(field, val) => {
2461 let mut inner = HashMap::new();
2462 inner.insert("endsWith".to_string(), val.clone());
2463 let mut outer = HashMap::new();
2464 outer.insert(field.clone(), Value::Object(inner));
2465 Value::Object(outer)
2466 }
2467 Filter::Matches(field, val) => {
2468 let mut inner = HashMap::new();
2469 inner.insert("matches".to_string(), val.clone());
2470 let mut outer = HashMap::new();
2471 outer.insert(field.clone(), Value::Object(inner));
2472 Value::Object(outer)
2473 }
2474 Filter::IsNull(field) => {
2475 let mut inner = HashMap::new();
2476 inner.insert("isNull".to_string(), Value::Boolean(true));
2477 let mut outer = HashMap::new();
2478 outer.insert(field.clone(), Value::Object(inner));
2479 Value::Object(outer)
2480 }
2481 Filter::IsNotNull(field) => {
2482 let mut inner = HashMap::new();
2483 inner.insert("isNotNull".to_string(), Value::Boolean(true));
2484 let mut outer = HashMap::new();
2485 outer.insert(field.clone(), Value::Object(inner));
2486 Value::Object(outer)
2487 }
2488 Filter::And(filters) => {
2489 let arr: Vec<Value> = filters.iter().map(filter_to_value).collect();
2490 let mut map = HashMap::new();
2491 map.insert("and".to_string(), Value::Array(arr));
2492 Value::Object(map)
2493 }
2494 Filter::Or(filters) => {
2495 let arr: Vec<Value> = filters.iter().map(filter_to_value).collect();
2496 let mut map = HashMap::new();
2497 map.insert("or".to_string(), Value::Array(arr));
2498 Value::Object(map)
2499 }
2500 Filter::Not(inner) => {
2501 let mut map = HashMap::new();
2502 map.insert("not".to_string(), filter_to_value(inner));
2503 Value::Object(map)
2504 }
2505 }
2506}
2507
2508fn parse_fragment_definition(pair: pest::iterators::Pair<Rule>) -> Result<ast::FragmentDef> {
2510 let mut name = String::new();
2511 let mut type_condition = String::new();
2512 let mut selection_set = Vec::new();
2513
2514 for inner in pair.into_inner() {
2515 match inner.as_rule() {
2516 Rule::identifier | Rule::keyword => {
2517 if name.is_empty() {
2518 name = inner.as_str().to_string();
2519 } else {
2520 type_condition = inner.as_str().to_string();
2521 }
2522 }
2523 Rule::selection_set => {
2524 selection_set = parse_selection_set(inner)?;
2525 }
2526 _ => {}
2527 }
2528 }
2529
2530 Ok(ast::FragmentDef {
2531 name,
2532 type_condition,
2533 selection_set,
2534 })
2535}
2536
2537fn parse_introspection_query(pair: pest::iterators::Pair<Rule>) -> Result<ast::IntrospectionQuery> {
2538 let mut arguments = Vec::new();
2539 let mut fields = Vec::new();
2540
2541 for inner in pair.into_inner() {
2542 match inner.as_rule() {
2543 Rule::arguments => {
2544 arguments = parse_arguments(inner)?;
2545 }
2546 Rule::introspection_fields => {
2547 for field in inner.into_inner() {
2548 if field.as_rule() == Rule::introspection_field {
2549 fields.push(field.as_str().to_string());
2550 }
2551 }
2552 }
2553 _ => {}
2554 }
2555 }
2556
2557 Ok(ast::IntrospectionQuery { arguments, fields })
2558}
2559
2560fn parse_lookup_selection(pair: pest::iterators::Pair<Rule>) -> Result<ast::LookupSelection> {
2561 let mut collection = String::new();
2562 let mut local_field = String::new();
2563 let mut foreign_field = String::new();
2564 let mut filter = None;
2565 let mut selection_set = Vec::new();
2566
2567 for inner in pair.into_inner() {
2568 match inner.as_rule() {
2569 Rule::lookup_args => {
2570 for arg in inner.into_inner() {
2571 match arg.as_rule() {
2572 Rule::string => {
2573 let s = arg.as_str();
2574 let unquoted = &s[1..s.len() - 1];
2575 if collection.is_empty() {
2576 collection = unquoted.to_string();
2577 } else if local_field.is_empty() {
2578 local_field = unquoted.to_string();
2579 } else if foreign_field.is_empty() {
2580 foreign_field = unquoted.to_string();
2581 }
2582 }
2583 Rule::filter_object => {
2584 if let Ok(filter_value) = parse_filter_object_to_value(arg) {
2585 filter = Some(value_to_filter(filter_value)?);
2586 }
2587 }
2588 _ => {}
2589 }
2590 }
2591 }
2592 Rule::sub_selection => {
2593 for sel in inner.into_inner() {
2594 if sel.as_rule() == Rule::selection_set {
2595 let fields = parse_selection_set(sel)?;
2596 selection_set = fields;
2597 }
2598 }
2599 }
2600 _ => {}
2601 }
2602 }
2603
2604 if collection.is_empty() || local_field.is_empty() || foreign_field.is_empty() {
2605 return Err(AqlError::new(
2606 ErrorCode::InvalidQuery,
2607 "Lookup must specify collection, localField, and foreignField".to_string(),
2608 ));
2609 }
2610
2611 Ok(ast::LookupSelection {
2612 collection,
2613 local_field,
2614 foreign_field,
2615 filter,
2616 selection_set,
2617 })
2618}
2619
2620fn parse_handler_operation(pair: pest::iterators::Pair<Rule>) -> Result<ast::HandlerDef> {
2622 let mut name = String::new();
2623 let mut trigger = ast::HandlerTrigger::Custom(String::new());
2624 let mut action: Option<ast::MutationOperation> = None;
2625
2626 for inner in pair.into_inner() {
2627 match inner.as_rule() {
2628 Rule::string => {
2629 let s = inner.as_str();
2630 name = s[1..s.len() - 1].to_string();
2631 }
2632 Rule::handler_body => {
2633 for body in inner.into_inner() {
2634 match body.as_rule() {
2635 Rule::handler_trigger => {
2636 for t in body.into_inner() {
2638 if t.as_rule() == Rule::string {
2639 let raw = t.as_str();
2640 let s = &raw[1..raw.len() - 1];
2641 trigger = parse_trigger_string(s);
2642 }
2643 }
2644 }
2645 Rule::handler_action => {
2646 for a in body.into_inner() {
2648 if a.as_rule() == Rule::mutation_call {
2649 if let Ok((op, sel)) = parse_mutation_call(a) {
2650 action = Some(ast::MutationOperation {
2651 alias: None,
2652 operation: op,
2653 directives: vec![],
2654 selection_set: sel,
2655 });
2656 }
2657 }
2658 }
2659 }
2660 _ => {}
2661 }
2662 }
2663 }
2664 _ => {}
2665 }
2666 }
2667
2668 let action = action.ok_or_else(|| {
2669 AqlError::new(ErrorCode::InvalidQuery, "Handler must have an action".to_string())
2670 })?;
2671
2672 Ok(ast::HandlerDef { name, trigger, action })
2673}
2674
2675fn parse_trigger_string(s: &str) -> ast::HandlerTrigger {
2676 let parts: Vec<&str> = s.splitn(2, ' ').collect();
2677 let event_type = parts[0].to_lowercase();
2678 let collection = parts.get(1).map(|c| c.to_string());
2679 match event_type.as_str() {
2680 "insert" => ast::HandlerTrigger::Insert { collection },
2681 "update" => ast::HandlerTrigger::Update { collection },
2682 "delete" => ast::HandlerTrigger::Delete { collection },
2683 "job_completed" | "jobcompleted" => ast::HandlerTrigger::JobCompleted,
2684 "job_failed" | "jobfailed" => ast::HandlerTrigger::JobFailed,
2685 _ => ast::HandlerTrigger::Custom(s.to_string()),
2686 }
2687}
2688
2689#[cfg(test)]
2690mod tests {
2691 use super::*;
2692
2693 #[test]
2694 fn test_parse_simple_query() {
2695 let query = r#"query { users { id name email } }"#;
2696 let result = parse(query);
2697 assert!(result.is_ok());
2698 let doc = result.unwrap();
2699 assert_eq!(doc.operations.len(), 1);
2700 }
2701
2702 #[test]
2703 fn test_parse_query_with_filter() {
2704 let query =
2705 r#"query GetActiveUsers { users(where: { active: { eq: true } }) { id name } }"#;
2706 let result = parse(query);
2707 assert!(result.is_ok());
2708 }
2709
2710 #[test]
2711 fn test_parse_mutation() {
2712 let query =
2713 r#"mutation { insertInto(collection: "users", data: { name: "John" }) { id } }"#;
2714 let result = parse(query);
2715 assert!(result.is_ok());
2716 }
2717
2718 #[test]
2719 fn test_parse_alter_collection() {
2720 let schema = r#"
2721 schema {
2722 alter collection users {
2723 add age: Int
2724 drop legacy_field
2725 rename name to full_name
2726 modify active: Boolean
2727 }
2728 }
2729 "#;
2730 let result = parse(schema);
2731 assert!(
2732 result.is_ok(),
2733 "Failed to parse alter collection: {:?}",
2734 result.err()
2735 );
2736
2737 let doc = result.unwrap();
2738 if let Operation::Schema(Schema { operations }) = &doc.operations[0] {
2739 if let SchemaOp::AlterCollection { name, actions } = &operations[0] {
2740 assert_eq!(name, "users");
2741 assert_eq!(actions.len(), 4);
2742
2743 match &actions[0] {
2745 AlterAction::AddField { field, .. } => {
2746 assert_eq!(field.name, "age");
2747 assert_eq!(field.field_type.name, "Int");
2748 }
2749 _ => panic!("Expected AddField"),
2750 }
2751
2752 match &actions[1] {
2754 AlterAction::DropField(name) => assert_eq!(name, "legacy_field"),
2755 _ => panic!("Expected DropField"),
2756 }
2757
2758 match &actions[2] {
2760 AlterAction::RenameField { from, to } => {
2761 assert_eq!(from, "name");
2762 assert_eq!(to, "full_name");
2763 }
2764 _ => panic!("Expected RenameField"),
2765 }
2766
2767 match &actions[3] {
2769 AlterAction::ModifyField(field) => {
2770 assert_eq!(field.name, "active");
2771 assert_eq!(field.field_type.name, "Boolean");
2772 }
2773 _ => panic!("Expected ModifyField"),
2774 }
2775 } else {
2776 panic!("Expected AlterCollection operation");
2777 }
2778 } else {
2779 panic!("Expected Schema operation");
2780 }
2781 }
2782
2783 #[test]
2784 fn test_parse_fragment_definition() {
2785 let query = r#"
2786 fragment UserFields on User {
2787 id
2788 name
2789 email
2790 }
2791 "#;
2792 let result = parse(query);
2793 assert!(
2794 result.is_ok(),
2795 "Failed to parse fragment: {:?}",
2796 result.err()
2797 );
2798 let doc = result.unwrap();
2799 assert_eq!(doc.operations.len(), 1);
2800
2801 if let Operation::FragmentDefinition(frag) = &doc.operations[0] {
2802 assert_eq!(frag.name, "UserFields");
2803 assert_eq!(frag.type_condition, "User");
2804 assert_eq!(frag.selection_set.len(), 3);
2805 } else {
2806 panic!("Expected FragmentDefinition");
2807 }
2808 }
2809
2810 #[test]
2811 fn test_parse_introspection() {
2812 let query = r#"__schema { collections, fields }"#;
2813 let result = parse(query);
2814 assert!(
2815 result.is_ok(),
2816 "Failed to parse introspection: {:?}",
2817 result.err()
2818 );
2819 let doc = result.unwrap();
2820 assert_eq!(doc.operations.len(), 1);
2821
2822 if let Operation::Introspection(intro) = &doc.operations[0] {
2823 assert_eq!(intro.fields.len(), 2);
2824 } else {
2825 panic!("Expected Introspection");
2826 }
2827 }
2828
2829 #[test]
2830 fn test_parse_fragment_with_query() {
2831 let query = r#"
2832 fragment UserFields on User {
2833 id
2834 name
2835 }
2836
2837 query GetUsers {
2838 users {
2839 ...UserFields
2840 }
2841 }
2842 "#;
2843 let result = parse(query);
2844 assert!(
2845 result.is_ok(),
2846 "Failed to parse fragment with query: {:?}",
2847 result.err()
2848 );
2849 let doc = result.unwrap();
2850 assert_eq!(doc.operations.len(), 2);
2851 }
2852
2853 #[test]
2854 fn test_parse_aggregate_with_alias() {
2855 let query = r#"
2856 query {
2857 products {
2858 stats: aggregate {
2859 totalStock: sum(field: "stock")
2860 avgPrice: avg(field: "price")
2861 count
2862 }
2863 }
2864 }
2865 "#;
2866 let result = parse(query);
2867 assert!(
2868 result.is_ok(),
2869 "Failed to parse aggregate: {:?}",
2870 result.err()
2871 );
2872
2873 let doc = result.unwrap();
2874 if let Operation::Query(q) = &doc.operations[0] {
2875 if let Selection::Field(products_field) = &q.selection_set[0] {
2876 assert_eq!(products_field.name, "products");
2877
2878 if let Selection::Field(agg_field) = &products_field.selection_set[0] {
2880 assert_eq!(agg_field.alias, Some("stats".to_string()));
2881 assert_eq!(agg_field.name, "aggregate");
2882
2883 assert_eq!(agg_field.selection_set.len(), 3, "Expected 3 agg functions");
2885
2886 if let Selection::Field(total_stock) = &agg_field.selection_set[0] {
2888 assert_eq!(
2889 total_stock.alias,
2890 Some("totalStock".to_string()),
2891 "Expected totalStock alias"
2892 );
2893 assert_eq!(total_stock.name, "sum");
2894 assert_eq!(total_stock.arguments.len(), 1);
2895 assert_eq!(total_stock.arguments[0].name, "field");
2896 } else {
2897 panic!("Expected Field for total_stock");
2898 }
2899
2900 if let Selection::Field(count) = &agg_field.selection_set[2] {
2902 assert_eq!(count.name, "count");
2903 } else {
2904 panic!("Expected Field for count");
2905 }
2906 } else {
2907 panic!("Expected Field for aggregate");
2908 }
2909 } else {
2910 panic!("Expected Field for products");
2911 }
2912 } else {
2913 panic!("Expected Query operation");
2914 }
2915 }
2916
2917 #[test]
2918 fn test_parse_lookup_selection() {
2919 let query = r#"
2920 query {
2921 orders {
2922 id
2923 total
2924 lookup(collection: "users", localField: "user_id", foreignField: "id") {
2925 name
2926 email
2927 }
2928 }
2929 }
2930 "#;
2931 let result = parse(query);
2932 assert!(result.is_ok(), "Failed to parse lookup: {:?}", result.err());
2933
2934 let doc = result.unwrap();
2935 if let Operation::Query(q) = &doc.operations[0] {
2936 if let Selection::Field(orders_field) = &q.selection_set[0] {
2937 assert_eq!(orders_field.name, "orders");
2938
2939 let lookup_selection = orders_field
2941 .selection_set
2942 .iter()
2943 .find(|f| match f {
2944 Selection::Field(field) => field.name == "lookup",
2945 _ => false,
2946 })
2947 .expect("Should have lookup field");
2948
2949 if let Selection::Field(lookup_field) = lookup_selection {
2950 assert!(
2952 lookup_field
2953 .arguments
2954 .iter()
2955 .any(|a| a.name == "collection"),
2956 "Should have collection argument"
2957 );
2958 assert!(
2959 lookup_field
2960 .arguments
2961 .iter()
2962 .any(|a| a.name == "localField"),
2963 "Should have localField argument"
2964 );
2965 assert!(
2966 lookup_field
2967 .arguments
2968 .iter()
2969 .any(|a| a.name == "foreignField"),
2970 "Should have foreignField argument"
2971 );
2972
2973 let collection_arg = lookup_field
2975 .arguments
2976 .iter()
2977 .find(|a| a.name == "collection")
2978 .unwrap();
2979 if let ast::Value::String(val) = &collection_arg.value {
2980 assert_eq!(val, "users");
2981 } else {
2982 panic!("collection should be a string");
2983 }
2984
2985 assert_eq!(lookup_field.selection_set.len(), 2);
2987 if let Selection::Field(f) = &lookup_field.selection_set[0] {
2988 assert_eq!(f.name, "name");
2989 }
2990 if let Selection::Field(f) = &lookup_field.selection_set[1] {
2991 assert_eq!(f.name, "email");
2992 }
2993 } else {
2994 panic!("Lookup should be a Field");
2995 }
2996 } else {
2997 panic!("Expected Field for orders");
2998 }
2999 } else {
3000 panic!("Expected Query operation");
3001 }
3002 }
3003
3004 #[test]
3005 fn test_parse_lookup_with_filter() {
3006 let query = r#"
3007 query {
3008 orders {
3009 id
3010 lookup(collection: "users", localField: "user_id", foreignField: "id", where: { active: { eq: true } }) {
3011 name
3012 }
3013 }
3014 }
3015 "#;
3016 let result = parse(query);
3017 assert!(
3018 result.is_ok(),
3019 "Failed to parse lookup with filter: {:?}",
3020 result.err()
3021 );
3022
3023 let doc = result.unwrap();
3024 if let Operation::Query(q) = &doc.operations[0] {
3025 if let Selection::Field(orders_field) = &q.selection_set[0] {
3026 let lookup_selection = orders_field
3027 .selection_set
3028 .iter()
3029 .find(|f| match f {
3030 Selection::Field(field) => field.name == "lookup",
3031 _ => false,
3032 })
3033 .expect("Should have lookup field");
3034
3035 if let Selection::Field(lookup_field) = lookup_selection {
3036 assert!(
3038 lookup_field.arguments.iter().any(|a| a.name == "where"),
3039 "Should have where argument for filter"
3040 );
3041 } else {
3042 panic!("Expected Field for lookup");
3043 }
3044 } else {
3045 panic!("Expected Field for orders");
3046 }
3047 } else {
3048 panic!("Expected Query operation");
3049 }
3050 }
3051}