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