Skip to main content

aurora_db/parser/
mod.rs

1//! Aurora Query Language (AQL) Parser
2//!
3//! Provides parsing for AQL using Pest grammar.
4
5use 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
22/// Reserved keywords that cannot be used as field/collection names
23const 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
87/// Generate a helpful error message, with specific detection for reserved keywords
88fn get_helpful_error_message(input: &str, line: usize, col: usize) -> String {
89    // Try to extract the token at the error location
90    if let Some(line_content) = input.lines().nth(line.saturating_sub(1)) {
91        // Extract the word at or near the column position
92        let chars: Vec<char> = line_content.chars().collect();
93        if col > 0 && col <= chars.len() {
94            // Find word boundaries around the column
95            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            // Check if the word is a reserved keyword
108            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    // Default message if no reserved keyword detected
123    format!("Syntax error at line {}, column {}", line, col)
124}
125
126/// Parse an AQL query string into an AST Document
127pub 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        // Log full error details internally for debugging
135        #[cfg(debug_assertions)]
136        eprintln!("Parse error details: {}", e);
137
138        // Try to extract the problematic token from the error location
139        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
169/// Parse an AQL query with variable values
170pub 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                    // Crude check, relying on grammar structure
346                    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
429// Re-implementing correctly below with keyword checking
430fn 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
472// MIGRATION PARSING
473
474fn 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                    // Strip surrounding quotes for string literals; use as-is for
494                    // numbers and bare identifiers.
495                    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                // Parse filter_object to Value then convert to Filter
557                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
575/// Parse a filter_object grammar rule to an AST Value
576fn 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
592/// Parse a filter_field into the map
593fn 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
630/// Parse a logical operator (and/or/not) into the map
631fn 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
668/// Parse a filter_condition into the map
669fn 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
685/// Parse a filter_operator into the map
686fn 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
954/// Parse a selection (field, fragment spread, or inline fragment)
955fn parse_selection(pair: pest::iterators::Pair<Rule>) -> Result<Selection> {
956    // The rule is `field`, which can contain fragment_spread, inline_fragment, etc.
957    // field = { fragment_spread | inline_fragment | aggregate_with_alias | ... | alias_name? ~ identifier ... }
958
959    // Check first child to determine type if it's a direct alternative
960    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            // It's a field (regular or special)
984            return Ok(Selection::Field(parse_field_inner(pair)?));
985        }
986    }
987}
988
989/// Helper to parse the field content into a Field struct
990fn 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                            // identifier ":" "windowFunc" "(" window_args ")"
1144                            let mut parts = sel.into_inner();
1145                            // First token is the alias identifier
1146                            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                                    // window_args: field, function, windowSize
1169                                    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
1194/// Parse an aggregate field (count or function like sum(field: "x"))
1195/// Supports optional aliases like `totalStock: sum(field: "stock")`
1196fn 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                // Extract alias from aggregate_field_alias (contains identifier ~ ":")
1206                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                // Contains either "count" or aggregate_function
1214                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                            // Parse function like sum(field: "age")
1219                            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                                        // Parse field: "name" or fields: [...]
1226                                        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                                                    // Check for "field" or "fields" keyword
1246                                                    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                            // Should be "count" literal
1268                        }
1269                    }
1270                }
1271
1272                // If no aggregate_function found, check if it's "count" literal
1273                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    // Fallback: If name is still empty but the raw text contains "count"
1285    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                                    // where_clause is handled elsewhere via filter_object
1665                                    _ => {}
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
1805/// Convert the formal `orderBy: { field: "name", direction: ASC }` (or multiple)
1806/// syntax into the same `Argument { name: "orderBy", value: Object({name: Enum("ASC")}) }`
1807/// shape that `extract_order_by` in the executor already understands.
1808fn 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
2007/// Unescape AQL string escape sequences: `\\` → `\`, `\"` → `"`,
2008/// `\/` → `/`, `\n` → newline, `\r` → CR, `\t` → tab, `\b` → BS,
2009/// `\f` → FF, `\uXXXX` → Unicode codepoint.
2010fn 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
2199/// Convert Filter back to Value (reverse of value_to_filter)
2200fn 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
2335// FRAGMENT AND INTROSPECTION PARSING
2336fn 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
2447/// Parse a handler definition: `define handler "name" { on: "insert coll", action: { ... } }`
2448fn 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                            // `on: "insert users"` or `on: "update users"` etc.
2464                            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                            // `action: { mutation_call }`
2474                            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                // Check add action
2571                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                // Check drop action
2580                match &actions[1] {
2581                    AlterAction::DropField(name) => assert_eq!(name, "legacy_field"),
2582                    _ => panic!("Expected DropField"),
2583                }
2584
2585                // Check rename action
2586                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                // Check modify action
2595                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                // Check inner selection set for aggregate
2706                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                    // Check aggregate selection set
2711                    assert_eq!(agg_field.selection_set.len(), 3, "Expected 3 agg functions");
2712
2713                    // Check first agg function
2714                    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                    // Check count
2728                    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                // Find the lookup selection
2767                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                    // Check lookup arguments were parsed
2778                    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                    // Check collection value
2801                    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                    // Check selection set
2813                    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                    // Check where argument exists
2864                    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}