Skip to main content

graphql_query/schema/
schema.rs

1use crate::ast::{ASTContext, DefaultIn, NamedType, OperationKind, Type};
2use bumpalo::collections::Vec;
3use bumpalo::Bump;
4use hashbrown::hash_map::DefaultHashBuilder;
5use hashbrown::{HashMap, HashSet};
6
7/// Schema Definition
8///
9/// A schema is created from root types for each kind of operation and is then used against
10/// AST documents for validation and execution. In this library the schema is never executable and
11/// serves only for metadata and type information. It is hence a "Client Schema".
12/// [Reference](https://spec.graphql.org/October2021/#sec-Schema)
13#[derive(Debug, Clone, PartialEq)]
14pub struct Schema<'a> {
15    pub(crate) query_type: Option<&'a SchemaObject<'a>>,
16    pub(crate) mutation_type: Option<&'a SchemaObject<'a>>,
17    pub(crate) subscription_type: Option<&'a SchemaObject<'a>>,
18    pub(crate) types:
19        hashbrown::HashMap<&'a str, &'a SchemaType<'a>, DefaultHashBuilder, &'a bumpalo::Bump>,
20}
21
22impl<'a> DefaultIn<'a> for Schema<'a> {
23    fn default_in(arena: &'a Bump) -> Self {
24        Schema {
25            query_type: None,
26            mutation_type: None,
27            subscription_type: None,
28            types: HashMap::new_in(arena),
29        }
30    }
31}
32
33impl<'a> Schema<'a> {
34    /// Returns whether the schema is a default, empty schema
35    pub fn is_empty(&self) -> bool {
36        self.types.is_empty()
37            && self.query_type.is_none()
38            && self.mutation_type.is_none()
39            && self.subscription_type.is_none()
40    }
41
42    /// Returns the root object type for query operations
43    #[inline]
44    pub fn query_type(&self) -> Option<&'a SchemaObject<'a>> {
45        self.query_type
46    }
47
48    /// Returns the root object type for mutation operations
49    #[inline]
50    pub fn mutation_type(&self) -> Option<&'a SchemaObject<'a>> {
51        self.mutation_type
52    }
53
54    /// Returns the root object type for subscription operations
55    #[inline]
56    pub fn subscription_type(&self) -> Option<&'a SchemaObject<'a>> {
57        self.subscription_type
58    }
59
60    /// Returns the appropriate object type depending on the passed operation kind
61    #[inline]
62    pub fn get_root_type(&self, operation_kind: OperationKind) -> Option<&'a SchemaObject<'a>> {
63        match operation_kind {
64            OperationKind::Query => self.query_type,
65            OperationKind::Mutation => self.mutation_type,
66            OperationKind::Subscription => self.subscription_type,
67        }
68    }
69
70    /// Retrieves a kind by name from known schema types.
71    #[inline]
72    pub fn get_type(&self, name: &'a str) -> Option<&'a SchemaType<'a>> {
73        self.types.get(name).copied()
74    }
75
76    /// Checks whether a given type is a sub type of another.
77    ///
78    /// This is typically used for return types of fields. A return type may be any given sub type
79    /// of the return type of said field.
80    pub fn is_sub_type(&self, abstract_type: SchemaType<'a>, sub_type: SchemaType<'a>) -> bool {
81        match abstract_type {
82            SchemaType::Union(schema_union) => schema_union.is_sub_type(sub_type),
83            SchemaType::Interface(schema_interface) => schema_interface.is_sub_type(sub_type),
84            SchemaType::Object(schema_object) => {
85                if let SchemaType::Object(sub_object_type) = sub_type {
86                    sub_object_type == schema_object
87                } else {
88                    false
89                }
90            }
91            _ => false,
92        }
93    }
94}
95
96/// Generic trait for any schema type that implements fields
97pub trait SchemaFields<'a>: Sized {
98    /// Add a new [SchemaField] to the list of fields
99    fn add_field(&mut self, ctx: &'a ASTContext, field: SchemaField<'a>);
100
101    /// Get a [Map] of all fields
102    fn get_fields(
103        &self,
104    ) -> HashMap<&'a str, &'a SchemaField<'a>, DefaultHashBuilder, &'a bumpalo::Bump>;
105
106    /// Get a known field by name
107    fn get_field(&self, name: &'a str) -> Option<&'a SchemaField<'a>> {
108        self.get_fields().get(name).copied()
109    }
110}
111
112/// Generic trait for any schema type that implements interfaces
113pub trait SchemaInterfaces<'a>: Sized {
114    /// Add a new [SchemaInterface] to the list of implemented interfaces
115    fn add_interface(&mut self, ctx: &'a ASTContext, interface: &'a str);
116
117    /// Get list of implemented [SchemaInterface]s
118    fn get_interfaces(&self) -> Vec<'a, &'a str>;
119
120    /// Checks whether given [ObjectType] is a possible subtype
121    #[inline]
122    fn implements_interface(&self, schema_interface: &SchemaInterface<'a>) -> bool {
123        self.get_interfaces()
124            .into_iter()
125            .any(|interface| interface == schema_interface.name)
126    }
127}
128
129/// Generic trait for any schema type that implements interfaces
130pub trait SchemaPossibleTypes<'a>: Sized {
131    /// Add a new [SchemaObject] to the list of possible types
132    fn add_possible_type(&mut self, ctx: &'a ASTContext, object: &'a str);
133
134    /// Get list of possible [SchemaObject] types
135    fn get_possible_types(&self) -> Vec<'a, &'a str>;
136
137    /// Get a specific possible type by name if it exists on the type
138    #[inline]
139    fn get_possible_type(&self, name: &'a str) -> Option<&'a str> {
140        self.get_possible_types()
141            .into_iter()
142            .find(|&possible_type| possible_type == name)
143    }
144
145    /// Checks whether given [ObjectType] is a possible subtype
146    #[inline]
147    fn is_possible_type(&self, schema_object: &SchemaObject<'a>) -> bool {
148        self.get_possible_types()
149            .into_iter()
150            .any(|possible_type| possible_type == schema_object.name)
151    }
152}
153
154/// Generic trait for any schema type that may be a super type of other types
155pub trait SchemaSuperType<'a>: Sized {
156    /// Checks whether a given type is a sub type of the current super type.
157    fn is_sub_type(&self, subtype: SchemaType<'a>) -> bool;
158}
159
160/// An Object type definition.
161///
162/// Most types in GraphQL are objects and define a set of fields and the interfaces they implement.
163/// [Reference](https://spec.graphql.org/October2021/#sec-Objects)
164#[derive(Debug, Clone, PartialEq)]
165pub struct SchemaObject<'a> {
166    pub name: &'a str,
167    pub(crate) fields: HashMap<&'a str, &'a SchemaField<'a>, DefaultHashBuilder, &'a bumpalo::Bump>,
168    pub(crate) interfaces: Vec<'a, &'a str>,
169}
170
171impl<'a> SchemaObject<'a> {
172    #[inline]
173    pub fn new(ctx: &'a ASTContext, name: &'a str) -> Self {
174        SchemaObject {
175            name,
176            fields: HashMap::new_in(&ctx.arena),
177            interfaces: Vec::new_in(&ctx.arena),
178        }
179    }
180}
181
182impl<'a> SchemaFields<'a> for SchemaObject<'a> {
183    /// Add a new [SchemaField] to the list of fields
184    fn add_field(&mut self, ctx: &'a ASTContext, field: SchemaField<'a>) {
185        self.fields.insert(field.name, ctx.alloc(field));
186    }
187
188    /// Get a [Map] of all fields on the [SchemaObject]
189    fn get_fields(
190        &self,
191    ) -> HashMap<&'a str, &'a SchemaField<'a>, DefaultHashBuilder, &'a bumpalo::Bump> {
192        self.fields.clone()
193    }
194}
195
196impl<'a> SchemaInterfaces<'a> for SchemaObject<'a> {
197    /// Add a new [SchemaInterface] to the list of implemented interfaces
198    fn add_interface(&mut self, _ctx: &'a ASTContext, interface: &'a str) {
199        // TODO: this used to prepend
200        self.interfaces.push(interface);
201    }
202
203    /// Get list of implemented [SchemaInterface]s
204    #[inline]
205    fn get_interfaces(&self) -> Vec<'a, &'a str> {
206        self.interfaces.clone()
207    }
208}
209
210/// An Interface type definition.
211///
212/// Any object or other interfaces may implement one or more interfaces and must then adhere to the
213/// definition of this interface. A field that returns an interface as its return type may return
214/// any object that implements this interface.
215/// [Reference](https://spec.graphql.org/October2021/#sec-Interfaces)
216#[derive(Clone, Debug)]
217pub struct SchemaInterface<'a> {
218    pub name: &'a str,
219    pub(crate) fields: HashMap<&'a str, &'a SchemaField<'a>, DefaultHashBuilder, &'a bumpalo::Bump>,
220    pub(crate) interfaces: Vec<'a, &'a str>,
221    pub(crate) possible_interfaces: Vec<'a, &'a str>,
222    pub(crate) possible_types: Vec<'a, &'a str>,
223}
224
225impl<'a> PartialEq for SchemaInterface<'a> {
226    fn eq(&self, other: &Self) -> bool {
227        self.name == other.name
228            && maps_are_equal(self.fields.clone(), other.fields.clone())
229            && self.interfaces == other.interfaces
230            && self.possible_interfaces == other.possible_interfaces
231            && self.possible_types == other.possible_types
232    }
233}
234
235impl<'a> SchemaInterface<'a> {
236    #[inline]
237    pub fn new(ctx: &'a ASTContext, name: &'a str) -> Self {
238        SchemaInterface {
239            name,
240            fields: HashMap::new_in(&ctx.arena),
241            interfaces: Vec::new_in(&ctx.arena),
242            possible_interfaces: Vec::new_in(&ctx.arena),
243            possible_types: Vec::new_in(&ctx.arena),
244        }
245    }
246
247    /// Add a new [SchemaInterface] to the list that implements this [SchemaInterface]
248    pub fn add_possible_interface(&mut self, _ctx: &'a ASTContext, interface: &'a str) {
249        self.possible_interfaces.push(interface);
250    }
251
252    /// Get list of possible [SchemaInterface]s that implement this [SchemaInterface]
253    #[inline]
254    pub fn get_possible_interfaces(&self) -> Vec<'a, &'a str> {
255        self.possible_interfaces.clone()
256    }
257}
258
259impl<'a> SchemaFields<'a> for SchemaInterface<'a> {
260    /// Add a new [SchemaField] to the list of fields
261    fn add_field(&mut self, ctx: &'a ASTContext, field: SchemaField<'a>) {
262        self.fields.insert(field.name, ctx.alloc(field));
263    }
264
265    /// Get a [Map] of all fields on the [SchemaInterface]
266    fn get_fields(
267        &self,
268    ) -> HashMap<&'a str, &'a SchemaField<'a>, DefaultHashBuilder, &'a bumpalo::Bump> {
269        self.fields.clone()
270    }
271}
272
273impl<'a> SchemaInterfaces<'a> for SchemaInterface<'a> {
274    /// Add a new [SchemaInterface] to the list of implemented interfaces
275    fn add_interface(&mut self, _ctx: &'a ASTContext, interface: &'a str) {
276        self.interfaces.push(interface);
277    }
278
279    /// Get list of implemented [SchemaInterface]s
280    #[inline]
281    fn get_interfaces(&self) -> Vec<'a, &'a str> {
282        self.interfaces.clone()
283    }
284}
285
286impl<'a> SchemaPossibleTypes<'a> for SchemaInterface<'a> {
287    /// Add a new [SchemaObject] to the list of possible types
288    fn add_possible_type(&mut self, _ctx: &'a ASTContext, object: &'a str) {
289        self.possible_types.push(object);
290    }
291
292    /// Get list of possible [SchemaObject] types
293    #[inline]
294    fn get_possible_types(&self) -> Vec<'a, &'a str> {
295        self.possible_types.clone()
296    }
297}
298
299impl<'a> SchemaSuperType<'a> for SchemaInterface<'a> {
300    #[inline]
301    fn is_sub_type(&self, sub_type: SchemaType<'a>) -> bool {
302        match sub_type {
303            SchemaType::Object(schema_object) => schema_object.implements_interface(self),
304            SchemaType::Interface(schema_interface) => schema_interface.implements_interface(self),
305            _ => false,
306        }
307    }
308}
309
310/// An object Field type definition.
311///
312/// A field is like a function that given its arguments as input values produces an output value.
313/// [Reference](https://spec.graphql.org/October2021/#FieldsDefinition)
314#[derive(Debug, Clone, PartialEq)]
315pub struct SchemaField<'a> {
316    pub name: &'a str,
317    pub arguments: HashMap<&'a str, SchemaInputField<'a>, DefaultHashBuilder, &'a bumpalo::Bump>,
318    pub output_type: &'a TypeRef<'a>,
319}
320
321impl<'a> SchemaField<'a> {
322    #[inline]
323    pub fn new(ctx: &'a ASTContext, name: &'a str, output_type: &'a TypeRef<'a>) -> Self {
324        SchemaField {
325            name,
326            arguments: HashMap::new_in(&ctx.arena),
327            output_type,
328        }
329    }
330
331    pub fn add_argument(&mut self, _ctx: &'a ASTContext, arg: SchemaInputField<'a>) {
332        self.arguments.insert(arg.name, arg);
333    }
334
335    #[inline]
336    pub fn get_argument(&self, name: &'a str) -> Option<&SchemaInputField<'a>> {
337        self.arguments.get(name)
338    }
339}
340
341/// A Union type definition.
342///
343/// A union contains a list of possible types that can be returned in its stead when its defined as
344/// an output type.
345/// [Reference](https://spec.graphql.org/October2021/#sec-Unions)
346#[derive(Debug, Clone)]
347pub struct SchemaUnion<'a> {
348    pub name: &'a str,
349    possible_types: Vec<'a, &'a str>,
350}
351
352impl<'a> PartialEq for SchemaUnion<'a> {
353    fn eq(&self, other: &Self) -> bool {
354        self.name == other.name && self.possible_types == other.possible_types
355    }
356}
357
358impl<'a> SchemaUnion<'a> {
359    #[inline]
360    pub fn new(ctx: &'a ASTContext, name: &'a str) -> Self {
361        SchemaUnion {
362            name,
363            possible_types: Vec::new_in(&ctx.arena),
364        }
365    }
366
367    #[inline]
368    pub fn is_sub_type(&self, sub_type: SchemaType<'a>) -> bool {
369        match sub_type {
370            SchemaType::Object(schema_object) => self
371                .possible_types
372                .iter()
373                .any(|possible| possible == &schema_object.name),
374            _ => false,
375        }
376    }
377}
378
379impl<'a> SchemaPossibleTypes<'a> for SchemaUnion<'a> {
380    /// Add a new [SchemaObject] to the list of possible types
381    fn add_possible_type(&mut self, _ctx: &'a ASTContext, object: &'a str) {
382        self.possible_types.push(object);
383    }
384
385    /// Get list of possible [SchemaObject] types
386    #[inline]
387    fn get_possible_types(&self) -> Vec<'a, &'a str> {
388        self.possible_types.clone()
389    }
390}
391
392impl<'a> SchemaSuperType<'a> for SchemaUnion<'a> {
393    #[inline]
394    fn is_sub_type(&self, sub_type: SchemaType<'a>) -> bool {
395        if let SchemaType::Object(schema_object) = sub_type {
396            self.is_possible_type(schema_object)
397        } else {
398            false
399        }
400    }
401}
402
403/// A Scalar type definition.
404///
405/// Scalars represent primitive leaf values in GraphQL that are represented with a specific
406/// serializer and deserializer, which makes the values returnable to a GraphQL client or readable
407/// by a GraphQL API.
408/// [Reference](https://spec.graphql.org/October2021/#sec-Scalars)
409#[derive(Debug, Clone, PartialEq)]
410pub struct SchemaScalar<'a> {
411    pub name: &'a str,
412}
413
414impl<'a> SchemaScalar<'a> {
415    #[inline]
416    pub fn new(name: &'a str) -> Self {
417        SchemaScalar { name }
418    }
419}
420
421#[derive(Debug, PartialEq, Clone)]
422pub struct SchemaEnum<'a> {
423    pub name: &'a str,
424    pub values: HashSet<&'a str, DefaultHashBuilder, &'a bumpalo::Bump>,
425}
426
427impl<'a> SchemaEnum<'a> {
428    #[inline]
429    pub fn new(ctx: &'a ASTContext, name: &'a str) -> Self {
430        SchemaEnum {
431            name,
432            values: HashSet::new_in(&ctx.arena),
433        }
434    }
435
436    pub fn add_value(&mut self, _ctx: &'a ASTContext, value: &'a str) {
437        self.values.insert(value);
438    }
439}
440
441/// An Input Object type definition.
442///
443/// Inputs, such as arguments, may sometimes be nested and accept objects that must adhere to the
444/// shape of an Input Object definition. This is often used to represent more complex inputs.
445/// [Reference](https://spec.graphql.org/October2021/#sec-Input-Objects)
446#[derive(Debug, Clone, PartialEq)]
447pub struct SchemaInputObject<'a> {
448    pub name: &'a str,
449    pub fields: HashMap<&'a str, SchemaInputField<'a>, DefaultHashBuilder, &'a bumpalo::Bump>,
450}
451
452impl<'a> SchemaInputObject<'a> {
453    #[inline]
454    pub fn new(ctx: &'a ASTContext, name: &'a str) -> Self {
455        SchemaInputObject {
456            name,
457            fields: HashMap::new_in(&ctx.arena),
458        }
459    }
460
461    pub fn add_field(&mut self, _ctx: &'a ASTContext, field: SchemaInputField<'a>) {
462        self.fields.insert(field.name, field);
463    }
464}
465
466#[derive(Debug, PartialEq, Clone, Copy)]
467pub struct SchemaInputField<'a> {
468    pub name: &'a str,
469    pub input_type: &'a TypeRef<'a>,
470}
471
472impl<'a> SchemaInputField<'a> {
473    #[inline]
474    pub fn new(name: &'a str, input_type: &'a TypeRef<'a>) -> Self {
475        SchemaInputField { name, input_type }
476    }
477}
478
479/// A named type enum that represents all possible GraphQL definition types.
480///
481/// [Reference](https://spec.graphql.org/October2021/#sec-Types)
482#[derive(Debug, PartialEq, Clone, Copy)]
483pub enum SchemaType<'a> {
484    InputObject(&'a SchemaInputObject<'a>),
485    Object(&'a SchemaObject<'a>),
486    Union(&'a SchemaUnion<'a>),
487    Interface(&'a SchemaInterface<'a>),
488    Scalar(&'a SchemaScalar<'a>),
489    Enum(&'a SchemaEnum<'a>),
490}
491
492impl<'a> SchemaType<'a> {
493    #[inline]
494    pub fn name(&self) -> &'a str {
495        match self {
496            SchemaType::InputObject(x) => x.name,
497            SchemaType::Object(x) => x.name,
498            SchemaType::Union(x) => x.name,
499            SchemaType::Interface(x) => x.name,
500            SchemaType::Scalar(x) => x.name,
501            SchemaType::Enum(x) => x.name,
502        }
503    }
504
505    pub fn object(&self) -> Option<&'a SchemaObject<'a>> {
506        match self {
507            SchemaType::Object(x) => Some(x),
508            _ => None,
509        }
510    }
511
512    pub fn input_object(&self) -> Option<&'a SchemaInputObject<'a>> {
513        match self {
514            SchemaType::InputObject(x) => Some(x),
515            _ => None,
516        }
517    }
518
519    pub fn interface(&self) -> Option<&'a SchemaInterface<'a>> {
520        match self {
521            SchemaType::Interface(x) => Some(x),
522            _ => None,
523        }
524    }
525
526    pub fn union_type(&self) -> Option<&'a SchemaUnion<'a>> {
527        match self {
528            SchemaType::Union(x) => Some(x),
529            _ => None,
530        }
531    }
532
533    pub fn input_type(&self) -> Option<InputType<'a>> {
534        match self {
535            SchemaType::Scalar(x) => Some(InputType::Scalar(x)),
536            SchemaType::Enum(x) => Some(InputType::Enum(x)),
537            SchemaType::InputObject(x) => Some(InputType::InputObject(x)),
538            _ => None,
539        }
540    }
541
542    pub fn output_type(&self) -> Option<OutputType<'a>> {
543        match self {
544            SchemaType::Object(x) => Some(OutputType::Object(x)),
545            SchemaType::Union(x) => Some(OutputType::Union(x)),
546            SchemaType::Interface(x) => Some(OutputType::Interface(x)),
547            SchemaType::Scalar(x) => Some(OutputType::Scalar(x)),
548            SchemaType::Enum(x) => Some(OutputType::Enum(x)),
549            _ => None,
550        }
551    }
552}
553
554#[derive(Debug, PartialEq, Clone)]
555pub enum OwnedSchemaType<'a> {
556    InputObject(SchemaInputObject<'a>),
557    Object(SchemaObject<'a>),
558    Union(SchemaUnion<'a>),
559    Interface(SchemaInterface<'a>),
560    Scalar(SchemaScalar<'a>),
561    Enum(SchemaEnum<'a>),
562}
563
564impl<'a> OwnedSchemaType<'a> {
565    #[inline]
566    pub fn name(&self) -> &'a str {
567        match self {
568            OwnedSchemaType::InputObject(x) => x.name,
569            OwnedSchemaType::Object(x) => x.name,
570            OwnedSchemaType::Union(x) => x.name,
571            OwnedSchemaType::Interface(x) => x.name,
572            OwnedSchemaType::Scalar(x) => x.name,
573            OwnedSchemaType::Enum(x) => x.name,
574        }
575    }
576
577    pub fn object(&'a self) -> Option<&'a SchemaObject<'a>> {
578        match self {
579            OwnedSchemaType::Object(x) => Some(x),
580            _ => None,
581        }
582    }
583
584    pub fn input_object(&'a self) -> Option<&'a SchemaInputObject<'a>> {
585        match self {
586            OwnedSchemaType::InputObject(x) => Some(x),
587            _ => None,
588        }
589    }
590
591    pub fn interface(&'a self) -> Option<&'a SchemaInterface<'a>> {
592        match self {
593            OwnedSchemaType::Interface(x) => Some(x),
594            _ => None,
595        }
596    }
597
598    pub fn union_type(&'a self) -> Option<&'a SchemaUnion<'a>> {
599        match self {
600            OwnedSchemaType::Union(x) => Some(x),
601            _ => None,
602        }
603    }
604
605    pub fn input_type(&'a self) -> Option<InputType<'a>> {
606        match self {
607            OwnedSchemaType::Scalar(x) => Some(InputType::Scalar(x)),
608            OwnedSchemaType::Enum(x) => Some(InputType::Enum(x)),
609            OwnedSchemaType::InputObject(x) => Some(InputType::InputObject(x)),
610            _ => None,
611        }
612    }
613
614    pub fn output_type(&'a self) -> Option<OutputType<'a>> {
615        match self {
616            OwnedSchemaType::Object(x) => Some(OutputType::Object(x)),
617            OwnedSchemaType::Union(x) => Some(OutputType::Union(x)),
618            OwnedSchemaType::Interface(x) => Some(OutputType::Interface(x)),
619            OwnedSchemaType::Scalar(x) => Some(OutputType::Scalar(x)),
620            OwnedSchemaType::Enum(x) => Some(OutputType::Enum(x)),
621            _ => None,
622        }
623    }
624}
625
626impl<'a> From<&'a SchemaObject<'a>> for SchemaType<'a> {
627    #[inline]
628    fn from(schema_object: &'a SchemaObject<'a>) -> Self {
629        SchemaType::Object(schema_object)
630    }
631}
632
633impl<'a> From<&'a SchemaUnion<'a>> for SchemaType<'a> {
634    #[inline]
635    fn from(schema_union: &'a SchemaUnion<'a>) -> Self {
636        SchemaType::Union(schema_union)
637    }
638}
639
640impl<'a> From<&'a SchemaInterface<'a>> for SchemaType<'a> {
641    #[inline]
642    fn from(schema_interface: &'a SchemaInterface<'a>) -> Self {
643        SchemaType::Interface(schema_interface)
644    }
645}
646
647impl<'a> From<OutputType<'a>> for SchemaType<'a> {
648    #[inline]
649    fn from(type_ref: OutputType<'a>) -> Self {
650        match type_ref {
651            OutputType::Object(x) => SchemaType::Object(x),
652            OutputType::Union(x) => SchemaType::Union(x),
653            OutputType::Interface(x) => SchemaType::Interface(x),
654            OutputType::Scalar(x) => SchemaType::Scalar(x),
655            OutputType::Enum(x) => SchemaType::Enum(x),
656        }
657    }
658}
659
660impl<'a> From<InputType<'a>> for SchemaType<'a> {
661    #[inline]
662    fn from(type_ref: InputType<'a>) -> Self {
663        match type_ref {
664            InputType::InputObject(x) => SchemaType::InputObject(x),
665            InputType::Scalar(x) => SchemaType::Scalar(x),
666            InputType::Enum(x) => SchemaType::Enum(x),
667        }
668    }
669}
670
671/// An output type enum that represents all possible GraphQL definition types that a field may
672/// return.
673///
674/// [Reference](https://spec.graphql.org/October2021/#sec-Input-and-Output-Types)
675#[derive(Debug, PartialEq, Clone, Copy)]
676pub enum OutputType<'a> {
677    Object(&'a SchemaObject<'a>),
678    Union(&'a SchemaUnion<'a>),
679    Interface(&'a SchemaInterface<'a>),
680    Scalar(&'a SchemaScalar<'a>),
681    Enum(&'a SchemaEnum<'a>),
682}
683
684impl<'a> OutputType<'a> {
685    #[inline]
686    pub fn name(&self) -> &str {
687        match self {
688            OutputType::Object(x) => x.name,
689            OutputType::Union(x) => x.name,
690            OutputType::Interface(x) => x.name,
691            OutputType::Scalar(x) => x.name,
692            OutputType::Enum(x) => x.name,
693        }
694    }
695
696    #[inline]
697    pub fn into_schema_type(&self) -> SchemaType<'a> {
698        match self {
699            OutputType::Object(x) => SchemaType::Object(x),
700            OutputType::Union(x) => SchemaType::Union(x),
701            OutputType::Interface(x) => SchemaType::Interface(x),
702            OutputType::Scalar(x) => SchemaType::Scalar(x),
703            OutputType::Enum(x) => SchemaType::Enum(x),
704        }
705    }
706}
707
708/// An input type enum that represents all possible GraphQL definition types that an argument or
709/// input object field may accept.
710///
711/// [Reference](https://spec.graphql.org/October2021/#sec-Input-and-Output-Types)
712#[derive(Debug, PartialEq, Clone, Copy)]
713pub enum InputType<'a> {
714    InputObject(&'a SchemaInputObject<'a>),
715    Scalar(&'a SchemaScalar<'a>),
716    Enum(&'a SchemaEnum<'a>),
717}
718
719impl<'a> InputType<'a> {
720    #[inline]
721    pub fn name(&self) -> &str {
722        match self {
723            InputType::InputObject(o) => o.name,
724            InputType::Scalar(s) => s.name,
725            InputType::Enum(e) => e.name,
726        }
727    }
728
729    #[inline]
730    pub fn named_type(&self) -> SchemaType<'a> {
731        match self {
732            InputType::InputObject(x) => SchemaType::InputObject(x),
733            InputType::Scalar(x) => SchemaType::Scalar(x),
734            InputType::Enum(x) => SchemaType::Enum(x),
735        }
736    }
737}
738
739#[derive(Clone, Copy)]
740pub enum TypeRef<'a> {
741    Type(&'a str),
742    ListType(&'a TypeRef<'a>),
743    NonNullType(&'a TypeRef<'a>),
744}
745
746impl<'a> TypeRef<'a> {
747    #[inline]
748    pub fn of_type(&self, schema: &'a Schema<'a>) -> &'a SchemaType<'a> {
749        match self {
750            TypeRef::Type(of_type) => {
751                let schema_type = schema
752                    .get_type(of_type)
753                    .expect("Referenced type should exist in the schema.");
754                schema_type
755            }
756            TypeRef::ListType(of_type) => of_type.of_type(schema),
757            TypeRef::NonNullType(of_type) => of_type.of_type(schema),
758        }
759    }
760}
761
762/// This implementation is necessary to circuit break circular types.
763/// Without this impl, `Debug` would print on and on, overflowing the stack as it's bouncing between types over and over.
764impl<'a> std::fmt::Debug for TypeRef<'a> {
765    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
766        match self {
767            Self::Type(arg0) => f.debug_tuple("OutputTypeRef").field(&arg0).finish(),
768            Self::ListType(arg0) => f.debug_tuple("ListType").field(arg0).finish(),
769            Self::NonNullType(arg0) => f.debug_tuple("NonNullType").field(arg0).finish(),
770        }
771    }
772}
773
774/// This implementation is necessary to circuit break circular types.
775/// Without this impl, `PartialEq` would never stop comparing types referencing each other.
776/// We achieve this by only ever comparing type names, which is all we need for comparing references.
777impl<'a> PartialEq for TypeRef<'a> {
778    fn eq(&self, other: &Self) -> bool {
779        match (self, other) {
780            (Self::Type(left), Self::Type(right)) => left == right,
781            (Self::ListType(left), Self::ListType(right)) => left == right,
782            (Self::NonNullType(left), Self::NonNullType(right)) => left == right,
783            _ => false,
784        }
785    }
786}
787
788/// By default, Toolshed Maps also compare insertion order. This utility compares only entries, not ordering.
789fn maps_are_equal<'a, V>(
790    left: HashMap<&'a str, V, DefaultHashBuilder, &'a bumpalo::Bump>,
791    right: HashMap<&'a str, V, DefaultHashBuilder, &'a bumpalo::Bump>,
792) -> bool
793where
794    V: PartialEq + Copy,
795{
796    let length_matches = left.len() == right.len();
797
798    // If the length matches and all entries in `left` are contained in `right` and equal, we can consider the maps equal.
799    length_matches
800        && left.iter().all(|(k, left_val)| {
801            right
802                .get(k)
803                .map(|right_val| left_val == right_val)
804                .unwrap_or(false)
805        })
806}
807
808/// Helper trait to generalize comparisons (see `eq_named_lists`).
809pub trait Named {
810    fn name(&self) -> &str;
811}
812
813impl<'a> Named for &'a SchemaInterface<'a> {
814    fn name(&self) -> &str {
815        self.name
816    }
817}
818
819impl<'a> Named for &'a SchemaObject<'a> {
820    fn name(&self) -> &str {
821        self.name
822    }
823}
824
825impl<'a> Named for &'a SchemaUnion<'a> {
826    fn name(&self) -> &str {
827        self.name
828    }
829}
830
831pub fn schema_type_to_type<'arena>(
832    ctx: &'arena ASTContext,
833    schema: &'arena Schema,
834    schema_type: TypeRef<'arena>,
835) -> Type<'arena> {
836    match schema_type {
837        t @ TypeRef::Type(_) => match t.of_type(schema).input_type().expect("input schema type") {
838            InputType::InputObject(SchemaInputObject { name, .. })
839            | InputType::Scalar(SchemaScalar { name, .. })
840            | InputType::Enum(SchemaEnum { name, .. }) => Type::NamedType(NamedType { name }),
841        },
842        TypeRef::ListType(t) => Type::ListType(ctx.alloc(schema_type_to_type(ctx, schema, *t))),
843        TypeRef::NonNullType(t) => {
844            Type::NonNullType(ctx.alloc(schema_type_to_type(ctx, schema, *t)))
845        }
846    }
847}