Skip to main content

graphql_tools/parser/schema/
ast.rs

1use std::str::FromStr;
2
3use thiserror::Error;
4
5pub use crate::parser::common::{Directive, Text, Type, Value};
6use crate::parser::position::Pos;
7
8#[derive(Debug, Clone, Default, PartialEq)]
9pub struct Document<'a, T: Text<'a>> {
10    pub definitions: Vec<Definition<'a, T>>,
11}
12
13impl<'a, T: Text<'a>> Document<'a, T> {
14    pub fn new(definitions: Vec<Definition<'a, T>>) -> Self {
15        Document { definitions }
16    }
17
18    pub fn query_type_name(&self) -> Option<&T::Value> {
19        self.root_type_name("Query", |sd| &sd.query)
20    }
21
22    pub fn mutation_type_name(&self) -> Option<&T::Value> {
23        self.root_type_name("Mutation", |sd| &sd.mutation)
24    }
25
26    pub fn subscription_type_name(&self) -> Option<&T::Value> {
27        self.root_type_name("Subscription", |sd| &sd.subscription)
28    }
29
30    fn root_type_name(
31        &self,
32        default_name: &str,
33        schema_definition_field: impl for<'s> Fn(&'s SchemaDefinition<'a, T>) -> &'s Option<T::Value>,
34    ) -> Option<&T::Value> {
35        let mut fallback = None;
36
37        for definition in &self.definitions {
38            match definition {
39                Definition::SchemaDefinition(schema_definition) => {
40                    if let Some(name) = schema_definition_field(schema_definition).as_ref() {
41                        return Some(name);
42                    }
43                }
44                Definition::TypeDefinition(TypeDefinition::Object(object))
45                    if fallback.is_none() && object.name.as_ref() == default_name =>
46                {
47                    fallback = Some(&object.name);
48                }
49                _ => {}
50            }
51        }
52
53        fallback
54    }
55}
56
57impl<'a> Document<'a, String> {
58    pub fn into_static(self) -> Document<'static, String> {
59        // To support both reference and owned values in the AST,
60        // all string data is represented with the ::common::Str<'a, T: Text<'a>>
61        // wrapper type.
62        // This type must carry the liftetime of the schema string,
63        // and is stored in a PhantomData value on the Str type.
64        // When using owned String types, the actual lifetime of
65        // the Ast nodes is 'static, since no references are kept,
66        // but the nodes will still carry the input lifetime.
67        // To continue working with Document<String> in a owned fasion
68        // the lifetime needs to be transmuted to 'static.
69        //
70        // This is safe because no references are present.
71        // Just the PhantomData lifetime reference is transmuted away.
72        unsafe { std::mem::transmute::<_, Document<'static, String>>(self) }
73    }
74}
75
76#[derive(Debug, Clone, PartialEq)]
77pub enum Definition<'a, T: Text<'a>> {
78    SchemaDefinition(SchemaDefinition<'a, T>),
79    TypeDefinition(TypeDefinition<'a, T>),
80    TypeExtension(TypeExtension<'a, T>),
81    DirectiveDefinition(DirectiveDefinition<'a, T>),
82}
83
84#[derive(Debug, Clone, Default, PartialEq)]
85pub struct SchemaDefinition<'a, T: Text<'a>> {
86    pub position: Pos,
87    pub directives: Vec<Directive<'a, T>>,
88    pub query: Option<T::Value>,
89    pub mutation: Option<T::Value>,
90    pub subscription: Option<T::Value>,
91}
92
93#[derive(Debug, Clone, PartialEq)]
94pub enum TypeDefinition<'a, T: Text<'a>> {
95    Scalar(ScalarType<'a, T>),
96    Object(ObjectType<'a, T>),
97    Interface(InterfaceType<'a, T>),
98    Union(UnionType<'a, T>),
99    Enum(EnumType<'a, T>),
100    InputObject(InputObjectType<'a, T>),
101}
102
103#[derive(Debug, Clone, PartialEq)]
104pub enum TypeExtension<'a, T: Text<'a>> {
105    Scalar(ScalarTypeExtension<'a, T>),
106    Object(ObjectTypeExtension<'a, T>),
107    Interface(InterfaceTypeExtension<'a, T>),
108    Union(UnionTypeExtension<'a, T>),
109    Enum(EnumTypeExtension<'a, T>),
110    InputObject(InputObjectTypeExtension<'a, T>),
111}
112
113#[derive(Debug, Clone, PartialEq)]
114pub struct ScalarType<'a, T: Text<'a>> {
115    pub position: Pos,
116    pub description: Option<String>,
117    pub name: T::Value,
118    pub directives: Vec<Directive<'a, T>>,
119}
120
121impl<'a, T> ScalarType<'a, T>
122where
123    T: Text<'a>,
124{
125    pub fn new(name: T::Value) -> Self {
126        Self {
127            position: Pos::default(),
128            description: None,
129            name,
130            directives: vec![],
131        }
132    }
133}
134
135#[derive(Debug, Clone, PartialEq)]
136pub struct ScalarTypeExtension<'a, T: Text<'a>> {
137    pub position: Pos,
138    pub name: T::Value,
139    pub directives: Vec<Directive<'a, T>>,
140}
141
142impl<'a, T> ScalarTypeExtension<'a, T>
143where
144    T: Text<'a>,
145{
146    pub fn new(name: T::Value) -> Self {
147        Self {
148            position: Pos::default(),
149            name,
150            directives: vec![],
151        }
152    }
153}
154
155#[derive(Debug, Clone, PartialEq)]
156pub struct ObjectType<'a, T: Text<'a>> {
157    pub position: Pos,
158    pub description: Option<String>,
159    pub name: T::Value,
160    pub implements_interfaces: Vec<T::Value>,
161    pub directives: Vec<Directive<'a, T>>,
162    pub fields: Vec<Field<'a, T>>,
163}
164
165impl<'a, T> ObjectType<'a, T>
166where
167    T: Text<'a>,
168{
169    pub fn new(name: T::Value) -> Self {
170        Self {
171            position: Pos::default(),
172            description: None,
173            name,
174            implements_interfaces: vec![],
175            directives: vec![],
176            fields: vec![],
177        }
178    }
179}
180
181#[derive(Debug, Clone, PartialEq)]
182pub struct ObjectTypeExtension<'a, T: Text<'a>> {
183    pub position: Pos,
184    pub name: T::Value,
185    pub implements_interfaces: Vec<T::Value>,
186    pub directives: Vec<Directive<'a, T>>,
187    pub fields: Vec<Field<'a, T>>,
188}
189
190impl<'a, T> ObjectTypeExtension<'a, T>
191where
192    T: Text<'a>,
193{
194    pub fn new(name: T::Value) -> Self {
195        Self {
196            position: Pos::default(),
197            name,
198            implements_interfaces: vec![],
199            directives: vec![],
200            fields: vec![],
201        }
202    }
203}
204
205#[derive(Debug, Clone, PartialEq)]
206pub struct Field<'a, T: Text<'a>> {
207    pub position: Pos,
208    pub description: Option<String>,
209    pub name: T::Value,
210    pub arguments: Vec<InputValue<'a, T>>,
211    pub field_type: Type<'a, T>,
212    pub directives: Vec<Directive<'a, T>>,
213}
214
215#[derive(Debug, Clone, PartialEq)]
216pub struct InputValue<'a, T: Text<'a>> {
217    pub position: Pos,
218    pub description: Option<String>,
219    pub name: T::Value,
220    pub value_type: Type<'a, T>,
221    pub default_value: Option<Value<'a, T>>,
222    pub directives: Vec<Directive<'a, T>>,
223}
224
225#[derive(Debug, Clone, PartialEq)]
226pub struct InterfaceType<'a, T: Text<'a>> {
227    pub position: Pos,
228    pub description: Option<String>,
229    pub name: T::Value,
230    pub implements_interfaces: Vec<T::Value>,
231    pub directives: Vec<Directive<'a, T>>,
232    pub fields: Vec<Field<'a, T>>,
233}
234
235impl<'a, T> InterfaceType<'a, T>
236where
237    T: Text<'a>,
238{
239    pub fn new(name: T::Value) -> Self {
240        Self {
241            position: Pos::default(),
242            description: None,
243            name,
244            implements_interfaces: vec![],
245            directives: vec![],
246            fields: vec![],
247        }
248    }
249}
250
251#[derive(Debug, Clone, PartialEq)]
252pub struct InterfaceTypeExtension<'a, T: Text<'a>> {
253    pub position: Pos,
254    pub name: T::Value,
255    pub implements_interfaces: Vec<T::Value>,
256    pub directives: Vec<Directive<'a, T>>,
257    pub fields: Vec<Field<'a, T>>,
258}
259
260impl<'a, T> InterfaceTypeExtension<'a, T>
261where
262    T: Text<'a>,
263{
264    pub fn new(name: T::Value) -> Self {
265        Self {
266            position: Pos::default(),
267            name,
268            implements_interfaces: vec![],
269            directives: vec![],
270            fields: vec![],
271        }
272    }
273}
274
275#[derive(Debug, Clone, PartialEq)]
276pub struct UnionType<'a, T: Text<'a>> {
277    pub position: Pos,
278    pub description: Option<String>,
279    pub name: T::Value,
280    pub directives: Vec<Directive<'a, T>>,
281    pub types: Vec<T::Value>,
282}
283
284impl<'a, T> UnionType<'a, T>
285where
286    T: Text<'a>,
287{
288    pub fn new(name: T::Value) -> Self {
289        Self {
290            position: Pos::default(),
291            description: None,
292            name,
293            directives: vec![],
294            types: vec![],
295        }
296    }
297}
298
299#[derive(Debug, Clone, PartialEq)]
300pub struct UnionTypeExtension<'a, T: Text<'a>> {
301    pub position: Pos,
302    pub name: T::Value,
303    pub directives: Vec<Directive<'a, T>>,
304    pub types: Vec<T::Value>,
305}
306
307impl<'a, T> UnionTypeExtension<'a, T>
308where
309    T: Text<'a>,
310{
311    pub fn new(name: T::Value) -> Self {
312        Self {
313            position: Pos::default(),
314            name,
315            directives: vec![],
316            types: vec![],
317        }
318    }
319}
320
321#[derive(Debug, Clone, PartialEq)]
322pub struct EnumType<'a, T: Text<'a>> {
323    pub position: Pos,
324    pub description: Option<String>,
325    pub name: T::Value,
326    pub directives: Vec<Directive<'a, T>>,
327    pub values: Vec<EnumValue<'a, T>>,
328}
329
330impl<'a, T> EnumType<'a, T>
331where
332    T: Text<'a>,
333{
334    pub fn new(name: T::Value) -> Self {
335        Self {
336            position: Pos::default(),
337            description: None,
338            name,
339            directives: vec![],
340            values: vec![],
341        }
342    }
343}
344
345#[derive(Debug, Clone, PartialEq)]
346pub struct EnumValue<'a, T: Text<'a>> {
347    pub position: Pos,
348    pub description: Option<String>,
349    pub name: T::Value,
350    pub directives: Vec<Directive<'a, T>>,
351}
352
353impl<'a, T> EnumValue<'a, T>
354where
355    T: Text<'a>,
356{
357    pub fn new(name: T::Value) -> Self {
358        Self {
359            position: Pos::default(),
360            description: None,
361            name,
362            directives: vec![],
363        }
364    }
365}
366
367#[derive(Debug, Clone, PartialEq)]
368pub struct EnumTypeExtension<'a, T: Text<'a>> {
369    pub position: Pos,
370    pub name: T::Value,
371    pub directives: Vec<Directive<'a, T>>,
372    pub values: Vec<EnumValue<'a, T>>,
373}
374
375impl<'a, T> EnumTypeExtension<'a, T>
376where
377    T: Text<'a>,
378{
379    pub fn new(name: T::Value) -> Self {
380        Self {
381            position: Pos::default(),
382            name,
383            directives: vec![],
384            values: vec![],
385        }
386    }
387}
388
389#[derive(Debug, Clone, PartialEq)]
390pub struct InputObjectType<'a, T: Text<'a>> {
391    pub position: Pos,
392    pub description: Option<String>,
393    pub name: T::Value,
394    pub directives: Vec<Directive<'a, T>>,
395    pub fields: Vec<InputValue<'a, T>>,
396}
397
398impl<'a, T> InputObjectType<'a, T>
399where
400    T: Text<'a>,
401{
402    pub fn new(name: T::Value) -> Self {
403        Self {
404            position: Pos::default(),
405            description: None,
406            name,
407            directives: vec![],
408            fields: vec![],
409        }
410    }
411}
412
413#[derive(Debug, Clone, PartialEq)]
414pub struct InputObjectTypeExtension<'a, T: Text<'a>> {
415    pub position: Pos,
416    pub name: T::Value,
417    pub directives: Vec<Directive<'a, T>>,
418    pub fields: Vec<InputValue<'a, T>>,
419}
420
421impl<'a, T> InputObjectTypeExtension<'a, T>
422where
423    T: Text<'a>,
424{
425    pub fn new(name: T::Value) -> Self {
426        Self {
427            position: Pos::default(),
428            name,
429            directives: vec![],
430            fields: vec![],
431        }
432    }
433}
434
435#[derive(Debug, Clone, PartialEq, Eq)]
436pub enum DirectiveLocation {
437    // executable
438    Query,
439    Mutation,
440    Subscription,
441    Field,
442    FragmentDefinition,
443    FragmentSpread,
444    InlineFragment,
445
446    // type_system
447    Schema,
448    Scalar,
449    Object,
450    FieldDefinition,
451    ArgumentDefinition,
452    Interface,
453    Union,
454    Enum,
455    EnumValue,
456    InputObject,
457    InputFieldDefinition,
458    VariableDefinition,
459}
460
461#[derive(Debug, Clone, PartialEq)]
462pub struct DirectiveDefinition<'a, T: Text<'a>> {
463    pub position: Pos,
464    pub description: Option<String>,
465    pub name: T::Value,
466    pub arguments: Vec<InputValue<'a, T>>,
467    pub repeatable: bool,
468    pub locations: Vec<DirectiveLocation>,
469}
470
471impl<'a, T> DirectiveDefinition<'a, T>
472where
473    T: Text<'a>,
474{
475    pub fn new(name: T::Value) -> Self {
476        Self {
477            position: Pos::default(),
478            description: None,
479            name,
480            arguments: vec![],
481            repeatable: false,
482            locations: vec![],
483        }
484    }
485}
486
487impl DirectiveLocation {
488    /// Returns GraphQL syntax compatible name of the directive
489    pub fn as_str(&self) -> &'static str {
490        use self::DirectiveLocation::*;
491        match *self {
492            Query => "QUERY",
493            Mutation => "MUTATION",
494            Subscription => "SUBSCRIPTION",
495            Field => "FIELD",
496            FragmentDefinition => "FRAGMENT_DEFINITION",
497            FragmentSpread => "FRAGMENT_SPREAD",
498            InlineFragment => "INLINE_FRAGMENT",
499            Schema => "SCHEMA",
500            Scalar => "SCALAR",
501            Object => "OBJECT",
502            FieldDefinition => "FIELD_DEFINITION",
503            ArgumentDefinition => "ARGUMENT_DEFINITION",
504            Interface => "INTERFACE",
505            Union => "UNION",
506            Enum => "ENUM",
507            EnumValue => "ENUM_VALUE",
508            InputObject => "INPUT_OBJECT",
509            InputFieldDefinition => "INPUT_FIELD_DEFINITION",
510            VariableDefinition => "VARIABLE_DEFINITION",
511        }
512    }
513
514    /// Returns `true` if this location is for queries (execution)
515    pub fn is_query(&self) -> bool {
516        use self::DirectiveLocation::*;
517        match *self {
518            Query | Mutation | Subscription | Field | FragmentDefinition | FragmentSpread
519            | InlineFragment => true,
520
521            Schema | Scalar | Object | FieldDefinition | ArgumentDefinition | Interface | Union
522            | Enum | EnumValue | InputObject | InputFieldDefinition | VariableDefinition => false,
523        }
524    }
525
526    /// Returns `true` if this location is for schema
527    pub fn is_schema(&self) -> bool {
528        !self.is_query()
529    }
530}
531
532#[derive(Debug, Error)]
533#[error("invalid directive location")]
534pub struct InvalidDirectiveLocation;
535
536impl FromStr for DirectiveLocation {
537    type Err = InvalidDirectiveLocation;
538    fn from_str(s: &str) -> Result<DirectiveLocation, InvalidDirectiveLocation> {
539        use self::DirectiveLocation::*;
540        let val = match s {
541            "QUERY" => Query,
542            "MUTATION" => Mutation,
543            "SUBSCRIPTION" => Subscription,
544            "FIELD" => Field,
545            "FRAGMENT_DEFINITION" => FragmentDefinition,
546            "FRAGMENT_SPREAD" => FragmentSpread,
547            "INLINE_FRAGMENT" => InlineFragment,
548            "SCHEMA" => Schema,
549            "SCALAR" => Scalar,
550            "OBJECT" => Object,
551            "FIELD_DEFINITION" => FieldDefinition,
552            "ARGUMENT_DEFINITION" => ArgumentDefinition,
553            "INTERFACE" => Interface,
554            "UNION" => Union,
555            "ENUM" => Enum,
556            "ENUM_VALUE" => EnumValue,
557            "INPUT_OBJECT" => InputObject,
558            "INPUT_FIELD_DEFINITION" => InputFieldDefinition,
559            "VARIABLE_DEFINITION" => VariableDefinition,
560            _ => return Err(InvalidDirectiveLocation),
561        };
562
563        Ok(val)
564    }
565}