Skip to main content

apollo_parser/parser/grammar/
object.rs

1#![allow(clippy::needless_return)]
2
3use crate::parser::grammar::description;
4use crate::parser::grammar::directive;
5use crate::parser::grammar::field;
6use crate::parser::grammar::name;
7use crate::parser::grammar::ty;
8use crate::parser::grammar::value::Constness;
9use crate::Parser;
10use crate::SyntaxKind;
11use crate::TokenKind;
12use crate::S;
13use crate::T;
14
15/// See: https://spec.graphql.org/October2021/#ObjectTypeDefinition
16///
17/// *ObjectTypeDefinition*:
18///     Description? **type** Name ImplementsInterfaces? Directives[Const]? FieldsDefinition?
19pub(crate) fn object_type_definition(p: &mut Parser) {
20    let _g = p.start_node(SyntaxKind::OBJECT_TYPE_DEFINITION);
21
22    if let Some(TokenKind::StringValue) = p.peek() {
23        description::description(p);
24    }
25
26    if let Some("type") = p.peek_data() {
27        p.bump(SyntaxKind::type_KW);
28    }
29
30    match p.peek() {
31        Some(TokenKind::Name) => name::name(p),
32        _ => p.err("expected a name"),
33    }
34
35    if let Some(TokenKind::Name) = p.peek() {
36        if p.peek_data().unwrap() == "implements" {
37            implements_interfaces(p);
38        }
39    }
40
41    if let Some(T![@]) = p.peek() {
42        directive::directives(p, Constness::Const);
43    }
44
45    if let Some(T!['{']) = p.peek() {
46        field::fields_definition(p);
47    }
48}
49
50/// See: https://spec.graphql.org/October2021/#ObjectTypeExtension
51///
52/// *ObjectTypeExtension*:
53///     **extend** **type** Name ImplementsInterfaces? Directives[Const]? FieldsDefinition
54///     **extend** **type** Name ImplementsInterfaces? Directives[Const]?
55///     **extend** **type** Name ImplementsInterfaces
56pub(crate) fn object_type_extension(p: &mut Parser) {
57    let _g = p.start_node(SyntaxKind::OBJECT_TYPE_EXTENSION);
58    p.bump(SyntaxKind::extend_KW);
59    p.bump(SyntaxKind::type_KW);
60
61    // Use this variable to see if any of ImplementsInterfacs, Directives or
62    // FieldsDefinitions is provided. If none are present, we push an error.
63    let mut meets_requirements = false;
64
65    match p.peek() {
66        Some(TokenKind::Name) => name::name(p),
67        _ => p.err("expected a Name"),
68    }
69
70    if let Some("implements") = p.peek_data() {
71        meets_requirements = true;
72        implements_interfaces(p);
73    }
74
75    if let Some(T![@]) = p.peek() {
76        meets_requirements = true;
77        directive::directives(p, Constness::Const)
78    }
79
80    if let Some(T!['{']) = p.peek() {
81        meets_requirements = true;
82        field::fields_definition(p)
83    }
84
85    if !meets_requirements {
86        p.err("expected an Implements Interface, Directives or a Fields Definition");
87    }
88}
89
90/// See: https://spec.graphql.org/October2021/#ImplementsInterfaces
91///
92/// *ImplementsInterfaces*:
93///     **implements** **&**? NamedType
94///     ImplementsInterfaces **&** NamedType
95pub(crate) fn implements_interfaces(p: &mut Parser) {
96    let _g = p.start_node(SyntaxKind::IMPLEMENTS_INTERFACES);
97    p.bump(SyntaxKind::implements_KW);
98
99    p.parse_separated_list(T![&], S![&], |p| {
100        if let Some(TokenKind::Name) = p.peek() {
101            ty::named_type(p);
102        } else {
103            p.err("expected an Interface name");
104        }
105    });
106}
107
108#[cfg(test)]
109mod test {
110    use super::*;
111    use crate::cst;
112
113    #[test]
114    fn object_type_definition() {
115        let input = "
116type Business implements NamedEntity & ValuedEntity & CatEntity {
117  name: String
118}";
119        let parser = Parser::new(input);
120        let cst = parser.parse();
121        assert_eq!(0, cst.errors().len());
122
123        let doc = cst.document();
124
125        for def in doc.definitions() {
126            if let cst::Definition::ObjectTypeDefinition(interface_type) = def {
127                assert_eq!(interface_type.name().unwrap().text(), "Business");
128                for implements_interfaces in interface_type
129                    .implements_interfaces()
130                    .unwrap()
131                    .named_types()
132                {
133                    // NamedEntity ValuedEntity CatEntity
134                    println!("{}", implements_interfaces.name().unwrap().text());
135                }
136            }
137        }
138    }
139}