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