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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use crate::{
    parser::grammar::{enum_, input, interface, object, scalar, schema, union_},
    Parser,
};

pub(crate) fn extensions(p: &mut Parser) {
    // we already know the next node is 'extend', check for the node after that
    // to figure out which type system extension to apply.
    match p.peek_data_n(2).as_deref() {
        Some("schema") => schema::schema_extension(p),
        Some("scalar") => scalar::scalar_type_extension(p),
        Some("type") => object::object_type_extension(p),
        Some("interface") => interface::interface_type_extension(p),
        Some("union") => union_::union_type_extension(p),
        Some("enum") => enum_::enum_type_extension(p),
        Some("input") => input::input_object_type_extension(p),
        _ => p.err_and_pop("Invalid Type System Extension. This extension cannot be applied."),
    }
}

#[cfg(test)]

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

    #[test]
    fn it_queries_graphql_extensions() {
        let gql = r#"
extend schema {
    mutation: MyMutationType
}
extend scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")
extend type Business implements NamedEntity
extend interface NamedEntity {
    name: String
}
extend union SearchResult = Pet
extend enum Pet {
    GuineaPig
    Cat
}
extend input First @include(if: "first")
        "#;

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

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

        let doc = ast.document();

        for definition in doc.definitions() {
            match definition {
                ast::Definition::SchemaExtension(schema_ext) => {
                    let root_operation_type: Vec<String> = schema_ext
                        .root_operation_type_definitions()
                        .filter_map(|def| Some(def.named_type()?.name()?.text().to_string()))
                        .collect();
                    assert_eq!(
                        root_operation_type.as_slice(),
                        ["MyMutationType".to_string()]
                    )
                }
                ast::Definition::ScalarTypeExtension(scalar_ext) => {
                    assert_eq!(
                        scalar_ext
                            .name()
                            .expect("Cannot get scalar type extension name.")
                            .text()
                            .as_ref(),
                        "UUID"
                    );
                }
                ast::Definition::ObjectTypeExtension(obj_ext) => {
                    assert_eq!(
                        obj_ext
                            .name()
                            .expect("Cannot get object type extension name.")
                            .text()
                            .as_ref(),
                        "Business"
                    );
                }
                ast::Definition::InterfaceTypeExtension(interface_ext) => {
                    assert_eq!(
                        interface_ext
                            .name()
                            .expect("Cannot get interface type extension name.")
                            .text()
                            .as_ref(),
                        "NamedEntity"
                    );
                }
                ast::Definition::UnionTypeExtension(union_ext) => {
                    assert_eq!(
                        union_ext
                            .name()
                            .expect("Cannot get union type extension name.")
                            .text()
                            .as_ref(),
                        "SearchResult"
                    );
                }
                ast::Definition::EnumTypeExtension(enum_ext) => {
                    assert_eq!(
                        enum_ext
                            .name()
                            .expect("Cannot get enum type extension name.")
                            .text()
                            .as_ref(),
                        "Pet"
                    );
                }
                ast::Definition::InputObjectTypeExtension(input_object_ext) => {
                    assert_eq!(
                        input_object_ext
                            .name()
                            .expect("Cannot get input object type extension name.")
                            .text()
                            .as_ref(),
                        "First"
                    );
                }
                _ => unimplemented!(),
            }
        }
    }

    #[test]
    fn it_reports_an_error_for_invalid_type_system_extension() {
        let gql = r#"
extend Cat
        "#;

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

        assert!(ast.errors().len() == 2);
        assert_eq!(ast.document().definitions().count(), 0);
    }

    #[test]
    fn it_continuous_parsing_when_an_invalid_extension_is_given() {
        let gql = r#"
extend Cat

extend interface NamedEntity {
    name: String
}
        "#;

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

        assert!(ast.errors().len() == 2);
        assert_eq!(ast.document().definitions().count(), 1);
    }
}