1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use crate::parser::grammar::value::Constness;
use crate::parser::grammar::{directive, name, ty, value};
use crate::{Parser, SyntaxKind, TokenKind, S, T};

/// See: https://spec.graphql.org/October2021/#VariableDefinitions
///
/// *VariableDefinitions*:
///     **(** VariableDefinition* **)**
pub(crate) fn variable_definitions(p: &mut Parser) {
    let _g = p.start_node(SyntaxKind::VARIABLE_DEFINITIONS);
    p.bump(S!['(']);

    if let Some(T![$]) = p.peek() {
        variable_definition(p);
    } else {
        p.err("expected a Variable Definition")
    }
    while let Some(T![$]) = p.peek() {
        variable_definition(p);
    }

    p.expect(T![')'], S![')']);
}

/// See: https://spec.graphql.org/October2021/#VariableDefinition
///
/// *VariableDefinition*:
///     Variable **:** Type DefaultValue? Directives[Const]?
pub(crate) fn variable_definition(p: &mut Parser) {
    let _guard = p.start_node(SyntaxKind::VARIABLE_DEFINITION);
    variable(p);

    if let Some(T![:]) = p.peek() {
        p.bump(S![:]);
        if let Some(TokenKind::Name | TokenKind::LBracket) = p.peek() {
            ty::ty(p);
            if let Some(T![=]) = p.peek() {
                value::default_value(p);
            }
            if let Some(T![@]) = p.peek() {
                directive::directives(p, Constness::Const)
            }
        } else {
            p.err("expected a Type");
        }
    } else {
        p.err("expected a Name");
    }
}

/// See: https://spec.graphql.org/October2021/#Variable
///
/// *Variable*:
///     **$** Name
pub(crate) fn variable(p: &mut Parser) {
    let _g = p.start_node(SyntaxKind::VARIABLE);
    p.bump(S![$]);
    name::name(p);
}

#[cfg(test)]

mod test {
    use crate::{cst, Parser};

    #[test]
    fn it_accesses_variable_name_and_type() {
        let gql = r#"
query GroceryStoreTrip($budget: Int) {
    name
}
        "#;

        let parser = Parser::new(gql);
        let cst = parser.parse();

        assert!(cst.errors().len() == 0);

        let doc = cst.document();

        for definition in doc.definitions() {
            if let cst::Definition::OperationDefinition(op_def) = definition {
                for var in op_def
                    .variable_definitions()
                    .unwrap()
                    .variable_definitions()
                {
                    assert_eq!(
                        var.variable().unwrap().name().unwrap().text().as_ref(),
                        "budget"
                    );
                    if let cst::Type::NamedType(name) = var.ty().unwrap() {
                        assert_eq!(name.name().unwrap().text().as_ref(), "Int")
                    }
                }
            }
        }
    }
}