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