async_graphql/registry/
mod.rs

1mod cache_control;
2mod export_sdl;
3mod stringify_exec_doc;
4
5use std::{
6    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
7    fmt::{self, Display, Formatter, Write},
8    sync::Arc,
9};
10
11pub use cache_control::CacheControl;
12pub use export_sdl::SDLExportOptions;
13use indexmap::{map::IndexMap, set::IndexSet};
14
15pub use crate::model::{__DirectiveLocation, location_traits};
16use crate::{
17    Any, Context, ID, InputType, OutputType, Positioned, ServerResult, SubscriptionType, Value,
18    VisitorContext,
19    model::__Schema,
20    parser::types::{BaseType as ParsedBaseType, Field, Type as ParsedType, VariableDefinition},
21    schema::IntrospectionMode,
22};
23
24fn strip_brackets(type_name: &str) -> Option<&str> {
25    type_name
26        .strip_prefix('[')
27        .map(|rest| &rest[..rest.len() - 1])
28}
29
30#[derive(Clone, Copy, Eq, PartialEq, Debug)]
31pub enum MetaTypeName<'a> {
32    List(&'a str),
33    NonNull(&'a str),
34    Named(&'a str),
35}
36
37impl Display for MetaTypeName<'_> {
38    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
39        match self {
40            MetaTypeName::Named(name) => write!(f, "{}", name),
41            MetaTypeName::NonNull(name) => write!(f, "{}!", name),
42            MetaTypeName::List(name) => write!(f, "[{}]", name),
43        }
44    }
45}
46
47impl MetaTypeName<'_> {
48    #[inline]
49    pub fn create(type_name: &str) -> MetaTypeName {
50        if let Some(type_name) = type_name.strip_suffix('!') {
51            MetaTypeName::NonNull(type_name)
52        } else if let Some(type_name) = strip_brackets(type_name) {
53            MetaTypeName::List(type_name)
54        } else {
55            MetaTypeName::Named(type_name)
56        }
57    }
58
59    #[inline]
60    pub fn concrete_typename(type_name: &str) -> &str {
61        match MetaTypeName::create(type_name) {
62            MetaTypeName::List(type_name) => Self::concrete_typename(type_name),
63            MetaTypeName::NonNull(type_name) => Self::concrete_typename(type_name),
64            MetaTypeName::Named(type_name) => type_name,
65        }
66    }
67
68    #[inline]
69    pub fn is_non_null(&self) -> bool {
70        matches!(self, MetaTypeName::NonNull(_))
71    }
72
73    #[inline]
74    #[must_use]
75    pub fn unwrap_non_null(&self) -> Self {
76        match self {
77            MetaTypeName::NonNull(ty) => MetaTypeName::create(ty),
78            _ => *self,
79        }
80    }
81
82    #[inline]
83    pub fn is_subtype(&self, sub: &MetaTypeName<'_>) -> bool {
84        match (self, sub) {
85            (MetaTypeName::NonNull(super_type), MetaTypeName::NonNull(sub_type))
86            | (MetaTypeName::Named(super_type), MetaTypeName::NonNull(sub_type)) => {
87                MetaTypeName::create(super_type).is_subtype(&MetaTypeName::create(sub_type))
88            }
89            (MetaTypeName::Named(super_type), MetaTypeName::Named(sub_type)) => {
90                super_type == sub_type
91            }
92            (MetaTypeName::List(super_type), MetaTypeName::List(sub_type)) => {
93                MetaTypeName::create(super_type).is_subtype(&MetaTypeName::create(sub_type))
94            }
95            _ => false,
96        }
97    }
98
99    #[inline]
100    pub fn is_list(&self) -> bool {
101        match self {
102            MetaTypeName::List(_) => true,
103            MetaTypeName::NonNull(ty) => MetaTypeName::create(ty).is_list(),
104            MetaTypeName::Named(name) => name.ends_with(']'),
105        }
106    }
107}
108
109/// actual directive invocation on SDL definitions
110#[derive(Debug, Clone)]
111pub struct MetaDirectiveInvocation {
112    /// name of directive to invoke
113    pub name: String,
114    /// actual arguments passed to directive
115    pub args: IndexMap<String, Value>,
116}
117
118impl MetaDirectiveInvocation {
119    pub fn sdl(&self) -> String {
120        let formatted_args = if self.args.is_empty() {
121            String::new()
122        } else {
123            format!(
124                "({})",
125                self.args
126                    .iter()
127                    .map(|(name, value)| format!("{}: {}", name, value))
128                    .collect::<Vec<_>>()
129                    .join(", ")
130            )
131        };
132        format!("@{}{}", self.name, formatted_args)
133    }
134}
135
136/// Input value metadata
137#[derive(Clone)]
138pub struct MetaInputValue {
139    /// The name of the input value
140    pub name: String,
141    /// The description of the input value
142    pub description: Option<String>,
143    /// The type of the input value
144    pub ty: String,
145    /// Field deprecation
146    pub deprecation: Deprecation,
147    /// The default value of the input value
148    pub default_value: Option<String>,
149    /// A function that uses to check if the input value should be exported to
150    /// schemas
151    pub visible: Option<MetaVisibleFn>,
152    /// Indicate that an input object is not accessible from a supergraph when
153    /// using Apollo Federation
154    pub inaccessible: bool,
155    /// Arbitrary string metadata that will be propagated to the supergraph when
156    /// using Apollo Federation. This attribute is repeatable
157    pub tags: Vec<String>,
158    /// Indicate that an input object is secret
159    pub is_secret: bool,
160    /// Custom directive invocations
161    pub directive_invocations: Vec<MetaDirectiveInvocation>,
162}
163
164type ComputeComplexityFn = fn(
165    &VisitorContext<'_>,
166    &[Positioned<VariableDefinition>],
167    &Field,
168    usize,
169) -> ServerResult<usize>;
170
171#[derive(Debug, Clone, Default)]
172pub enum Deprecation {
173    #[default]
174    NoDeprecated,
175    Deprecated {
176        reason: Option<String>,
177    },
178}
179
180impl Deprecation {
181    #[inline]
182    pub fn is_deprecated(&self) -> bool {
183        matches!(self, Deprecation::Deprecated { .. })
184    }
185
186    #[inline]
187    pub fn reason(&self) -> Option<&str> {
188        match self {
189            Deprecation::NoDeprecated => None,
190            Deprecation::Deprecated { reason } => reason.as_deref(),
191        }
192    }
193}
194
195/// Field metadata
196#[derive(Clone)]
197pub struct MetaField {
198    /// The name of the field
199    pub name: String,
200    /// The description of the field
201    pub description: Option<String>,
202    /// The arguments of the field
203    pub args: IndexMap<String, MetaInputValue>,
204    /// The type of the field
205    pub ty: String,
206    /// Field deprecation
207    pub deprecation: Deprecation,
208    /// Used to create HTTP `Cache-Control` header
209    pub cache_control: CacheControl,
210    /// Mark a field as owned by another service. This allows service A to use
211    /// fields from service B while also knowing at runtime the types of that
212    /// field.
213    pub external: bool,
214    /// Annotate the required input fieldset from a base type for a resolver. It
215    /// is used to develop a query plan where the required fields may not be
216    /// needed by the client, but the service may need additional information
217    /// from other services.
218    pub requires: Option<String>,
219    /// Annotate the expected returned fieldset from a field on a base type that
220    /// is guaranteed to be selectable by the gateway.
221    pub provides: Option<String>,
222    /// A function that uses to check if the field should be exported to
223    /// schemas
224    pub visible: Option<MetaVisibleFn>,
225    /// Indicate that an object type's field is allowed to be resolved by
226    /// multiple subgraphs
227    pub shareable: bool,
228    /// Indicate that an object is not accessible from a supergraph when using
229    /// Apollo Federation
230    pub inaccessible: bool,
231    /// Arbitrary string metadata that will be propagated to the supergraph when
232    /// using Apollo Federation. This attribute is repeatable
233    pub tags: Vec<String>,
234    /// Mark the field as overriding a field currently present on another
235    /// subgraph. It is used to migrate fields between subgraphs.
236    pub override_from: Option<String>,
237    /// A constant or function to get the complexity
238    pub compute_complexity: Option<ComputeComplexityFn>,
239    /// Custom directive invocations
240    pub directive_invocations: Vec<MetaDirectiveInvocation>,
241    /// Indicates to composition that the target element is accessible only to
242    /// the authenticated supergraph users with the appropriate JWT scopes
243    /// when using Apollo Federation.
244    pub requires_scopes: Vec<String>,
245}
246
247#[derive(Clone)]
248pub struct MetaEnumValue {
249    pub name: String,
250    pub description: Option<String>,
251    pub deprecation: Deprecation,
252    pub visible: Option<MetaVisibleFn>,
253    pub inaccessible: bool,
254    pub tags: Vec<String>,
255    pub directive_invocations: Vec<MetaDirectiveInvocation>,
256}
257
258type MetaVisibleFn = fn(&Context<'_>) -> bool;
259
260#[derive(Debug, Copy, Clone, Eq, PartialEq)]
261pub enum MetaTypeId {
262    Scalar,
263    Object,
264    Interface,
265    Union,
266    Enum,
267    InputObject,
268}
269
270impl MetaTypeId {
271    fn create_fake_type(&self, rust_typename: &'static str) -> MetaType {
272        match self {
273            MetaTypeId::Scalar => MetaType::Scalar {
274                name: "".to_string(),
275                description: None,
276                is_valid: None,
277                visible: None,
278                inaccessible: false,
279                tags: vec![],
280                specified_by_url: None,
281                directive_invocations: vec![],
282                requires_scopes: vec![],
283            },
284            MetaTypeId::Object => MetaType::Object {
285                name: "".to_string(),
286                description: None,
287                fields: Default::default(),
288                cache_control: Default::default(),
289                extends: false,
290                shareable: false,
291                resolvable: true,
292                inaccessible: false,
293                interface_object: false,
294                tags: vec![],
295                keys: None,
296                visible: None,
297                is_subscription: false,
298                rust_typename: Some(rust_typename),
299                directive_invocations: vec![],
300                requires_scopes: vec![],
301            },
302            MetaTypeId::Interface => MetaType::Interface {
303                name: "".to_string(),
304                description: None,
305                fields: Default::default(),
306                possible_types: Default::default(),
307                extends: false,
308                inaccessible: false,
309                tags: vec![],
310                keys: None,
311                visible: None,
312                rust_typename: Some(rust_typename),
313                directive_invocations: vec![],
314                requires_scopes: vec![],
315            },
316            MetaTypeId::Union => MetaType::Union {
317                name: "".to_string(),
318                description: None,
319                possible_types: Default::default(),
320                visible: None,
321                inaccessible: false,
322                tags: vec![],
323                rust_typename: Some(rust_typename),
324                directive_invocations: vec![],
325            },
326            MetaTypeId::Enum => MetaType::Enum {
327                name: "".to_string(),
328                description: None,
329                enum_values: Default::default(),
330                visible: None,
331                inaccessible: false,
332                tags: vec![],
333                rust_typename: Some(rust_typename),
334                directive_invocations: vec![],
335                requires_scopes: vec![],
336            },
337            MetaTypeId::InputObject => MetaType::InputObject {
338                name: "".to_string(),
339                description: None,
340                input_fields: Default::default(),
341                visible: None,
342                inaccessible: false,
343                tags: vec![],
344                rust_typename: Some(rust_typename),
345                oneof: false,
346                directive_invocations: vec![],
347            },
348        }
349    }
350}
351
352impl Display for MetaTypeId {
353    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
354        f.write_str(match self {
355            MetaTypeId::Scalar => "Scalar",
356            MetaTypeId::Object => "Object",
357            MetaTypeId::Interface => "Interface",
358            MetaTypeId::Union => "Union",
359            MetaTypeId::Enum => "Enum",
360            MetaTypeId::InputObject => "InputObject",
361        })
362    }
363}
364
365/// A validator for scalar
366pub type ScalarValidatorFn = Arc<dyn Fn(&Value) -> bool + Send + Sync>;
367
368/// Type metadata
369#[derive(Clone)]
370pub enum MetaType {
371    /// Scalar
372    ///
373    /// Reference: <https://spec.graphql.org/October2021/#sec-Scalars>
374    Scalar {
375        /// The name of the scalar
376        name: String,
377        /// the description of the scalar
378        description: Option<String>,
379        /// A function that uses to check if the scalar is valid
380        is_valid: Option<ScalarValidatorFn>,
381        /// A function that uses to check if the scalar should be exported to
382        /// schemas
383        visible: Option<MetaVisibleFn>,
384        /// Indicate that a scalar is not accessible from a supergraph when
385        /// using Apollo Federation
386        ///
387        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#inaccessible>
388        inaccessible: bool,
389        /// Arbitrary string metadata that will be propagated to the supergraph
390        /// when using Apollo Federation. This attribute is repeatable
391        ///
392        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#applying-metadata>
393        tags: Vec<String>,
394        /// Provide a specification URL for this scalar type, it must link to a
395        /// human-readable specification of the data format, serialization and
396        /// coercion rules for this scalar.
397        specified_by_url: Option<String>,
398        /// custom directive invocations
399        directive_invocations: Vec<MetaDirectiveInvocation>,
400        /// Indicates to composition that the target element is accessible only
401        /// to the authenticated supergraph users with the appropriate
402        /// JWT scopes when using Apollo Federation.
403        requires_scopes: Vec<String>,
404    },
405    /// Object
406    ///
407    /// Reference: <https://spec.graphql.org/October2021/#sec-Objects>
408    Object {
409        /// The name of the object
410        name: String,
411        /// The description of the object
412        description: Option<String>,
413        /// The fields of the object type
414        fields: IndexMap<String, MetaField>,
415        /// Used to create HTTP `Cache-Control` header
416        cache_control: CacheControl,
417        /// Indicates that an object definition is an extension of another
418        /// definition of that same type.
419        ///
420        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#extends>
421        extends: bool,
422        /// Indicates that an object type's field is allowed to be resolved by
423        /// multiple subgraphs (by default in Federation 2, object fields can be
424        /// resolved by only one subgraph).
425        ///
426        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#shareable>
427        shareable: bool,
428        /// Indicates that the subgraph does not define a reference resolver
429        /// for this object. Objects are assumed to be resolvable by default.
430        ///
431        /// Most commonly used to reference an entity defined in another
432        /// subgraph without contributing fields. Part of the `@key` directive.
433        ///
434        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#key>
435        resolvable: bool,
436        /// The keys of the object type
437        ///
438        /// Designates an object type as an [entity](https://www.apollographql.com/docs/federation/entities) and specifies
439        /// its key fields (a set of fields that the subgraph can use to
440        /// uniquely identify any instance of the entity).
441        ///
442        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#key>
443        keys: Option<Vec<String>>,
444        /// A function that uses to check if the object should be exported to
445        /// schemas
446        visible: Option<MetaVisibleFn>,
447        /// Indicate that an object is not accessible from a supergraph when
448        /// using Apollo Federation
449        ///
450        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#inaccessible>
451        inaccessible: bool,
452        /// During composition, the fields of every `@interfaceObject` are added
453        /// both to their corresponding interface definition and to all
454        /// entity types that implement that interface.
455        ///
456        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#interfaceobject>
457        interface_object: bool,
458        /// Arbitrary string metadata that will be propagated to the supergraph
459        /// when using Apollo Federation. This attribute is repeatable
460        ///
461        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#applying-metadata>
462        tags: Vec<String>,
463        /// Indicates whether it is a subscription object
464        is_subscription: bool,
465        /// The Rust typename corresponding to the object
466        rust_typename: Option<&'static str>,
467        /// custom directive invocations
468        directive_invocations: Vec<MetaDirectiveInvocation>,
469        /// Indicates to composition that the target element is accessible only
470        /// to the authenticated supergraph users with the appropriate
471        /// JWT scopes when using Apollo Federation.
472        requires_scopes: Vec<String>,
473    },
474    /// Interface
475    ///
476    /// Reference: <https://spec.graphql.org/October2021/#sec-Interfaces>
477    Interface {
478        /// The name of the interface
479        name: String,
480        /// The description of the interface
481        description: Option<String>,
482        /// The fields of the interface
483        fields: IndexMap<String, MetaField>,
484        /// The object types that implement this interface
485        /// Add fields to an entity that's defined in another service
486        possible_types: IndexSet<String>,
487        /// Indicates that an interface definition is an extension of another
488        /// definition of that same type.
489        ///
490        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#extends>
491        extends: bool,
492        /// The keys of the object type
493        ///
494        /// Designates an object type as an [entity](https://www.apollographql.com/docs/federation/entities) and specifies
495        /// its key fields (a set of fields that the subgraph can use to
496        /// uniquely identify any instance of the entity).
497        ///
498        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#key>
499        keys: Option<Vec<String>>,
500        /// A function that uses to check if the interface should be exported to
501        /// schemas
502        visible: Option<MetaVisibleFn>,
503        /// Indicate that an interface is not accessible from a supergraph when
504        /// using Apollo Federation
505        ///
506        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#inaccessible>
507        inaccessible: bool,
508        /// Arbitrary string metadata that will be propagated to the supergraph
509        /// when using Apollo Federation. This attribute is repeatable
510        ///
511        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#applying-metadata>
512        tags: Vec<String>,
513        /// The Rust typename corresponding to the interface
514        rust_typename: Option<&'static str>,
515        /// custom directive invocations
516        directive_invocations: Vec<MetaDirectiveInvocation>,
517        /// Indicates to composition that the target element is accessible only
518        /// to the authenticated supergraph users with the appropriate
519        /// JWT scopes when using Apollo Federation.
520        requires_scopes: Vec<String>,
521    },
522    /// Union
523    ///
524    /// Reference: <https://spec.graphql.org/October2021/#sec-Unions>
525    Union {
526        /// The name of the interface
527        name: String,
528        /// The description of the union
529        description: Option<String>,
530        /// The object types that could be the union
531        possible_types: IndexSet<String>,
532        /// A function that uses to check if the union should be exported to
533        /// schemas
534        visible: Option<MetaVisibleFn>,
535        /// Indicate that an union is not accessible from a supergraph when
536        /// using Apollo Federation
537        ///
538        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#inaccessible>
539        inaccessible: bool,
540        /// Arbitrary string metadata that will be propagated to the supergraph
541        /// when using Apollo Federation. This attribute is repeatable
542        ///
543        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#applying-metadata>
544        tags: Vec<String>,
545        /// The Rust typename corresponding to the union
546        rust_typename: Option<&'static str>,
547        /// custom directive invocations
548        directive_invocations: Vec<MetaDirectiveInvocation>,
549    },
550    /// Enum
551    ///
552    /// Reference: <https://spec.graphql.org/October2021/#sec-Enums>
553    Enum {
554        /// The name of the enum
555        name: String,
556        /// The description of the enum
557        description: Option<String>,
558        /// The values of the enum
559        enum_values: IndexMap<String, MetaEnumValue>,
560        /// A function that uses to check if the enum should be exported to
561        /// schemas
562        visible: Option<MetaVisibleFn>,
563        /// Indicate that an enum is not accessible from a supergraph when
564        /// using Apollo Federation
565        ///
566        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#inaccessible>
567        inaccessible: bool,
568        /// Arbitrary string metadata that will be propagated to the supergraph
569        /// when using Apollo Federation. This attribute is repeatable
570        ///
571        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#applying-metadata>
572        tags: Vec<String>,
573        /// The Rust typename corresponding to the enum
574        rust_typename: Option<&'static str>,
575        /// custom directive invocations
576        directive_invocations: Vec<MetaDirectiveInvocation>,
577        /// Indicates to composition that the target element is accessible only
578        /// to the authenticated supergraph users with the appropriate
579        /// JWT scopes when using Apollo Federation.
580        requires_scopes: Vec<String>,
581    },
582    /// Input object
583    ///
584    /// Reference: <https://spec.graphql.org/October2021/#sec-Input-Objects>
585    InputObject {
586        /// The name of the input object
587        name: String,
588        /// The description of the input object
589        description: Option<String>,
590        /// The fields of the input object
591        input_fields: IndexMap<String, MetaInputValue>,
592        /// A function that uses to check if the input object should be exported
593        /// to schemas
594        visible: Option<MetaVisibleFn>,
595        /// Indicate that a input object is not accessible from a supergraph
596        /// when using Apollo Federation
597        ///
598        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#inaccessible>
599        inaccessible: bool,
600        /// Arbitrary string metadata that will be propagated to the supergraph
601        /// when using Apollo Federation. This attribute is repeatable
602        ///
603        /// Reference: <https://www.apollographql.com/docs/federation/federated-types/federated-directives/#applying-metadata>
604        tags: Vec<String>,
605        /// The Rust typename corresponding to the enum
606        rust_typename: Option<&'static str>,
607        /// Is the oneof input objects
608        ///
609        /// Reference: <https://github.com/graphql/graphql-spec/pull/825>
610        oneof: bool,
611        /// custom directive invocations
612        directive_invocations: Vec<MetaDirectiveInvocation>,
613    },
614}
615
616impl MetaType {
617    #[inline]
618    pub fn type_id(&self) -> MetaTypeId {
619        match self {
620            MetaType::Scalar { .. } => MetaTypeId::Scalar,
621            MetaType::Object { .. } => MetaTypeId::Object,
622            MetaType::Interface { .. } => MetaTypeId::Interface,
623            MetaType::Union { .. } => MetaTypeId::Union,
624            MetaType::Enum { .. } => MetaTypeId::Enum,
625            MetaType::InputObject { .. } => MetaTypeId::InputObject,
626        }
627    }
628
629    #[inline]
630    pub fn field_by_name(&self, name: &str) -> Option<&MetaField> {
631        self.fields().and_then(|fields| fields.get(name))
632    }
633
634    #[inline]
635    pub fn fields(&self) -> Option<&IndexMap<String, MetaField>> {
636        match self {
637            MetaType::Object { fields, .. } => Some(&fields),
638            MetaType::Interface { fields, .. } => Some(&fields),
639            _ => None,
640        }
641    }
642
643    #[inline]
644    pub fn is_visible(&self, ctx: &Context<'_>) -> bool {
645        let visible = match self {
646            MetaType::Scalar { visible, .. } => visible,
647            MetaType::Object { visible, .. } => visible,
648            MetaType::Interface { visible, .. } => visible,
649            MetaType::Union { visible, .. } => visible,
650            MetaType::Enum { visible, .. } => visible,
651            MetaType::InputObject { visible, .. } => visible,
652        };
653        is_visible(ctx, visible)
654    }
655
656    #[inline]
657    pub fn name(&self) -> &str {
658        match self {
659            MetaType::Scalar { name, .. } => &name,
660            MetaType::Object { name, .. } => name,
661            MetaType::Interface { name, .. } => name,
662            MetaType::Union { name, .. } => name,
663            MetaType::Enum { name, .. } => name,
664            MetaType::InputObject { name, .. } => name,
665        }
666    }
667
668    #[inline]
669    pub fn is_composite(&self) -> bool {
670        matches!(
671            self,
672            MetaType::Object { .. } | MetaType::Interface { .. } | MetaType::Union { .. }
673        )
674    }
675
676    #[inline]
677    pub fn is_abstract(&self) -> bool {
678        matches!(self, MetaType::Interface { .. } | MetaType::Union { .. })
679    }
680
681    #[inline]
682    pub fn is_leaf(&self) -> bool {
683        matches!(self, MetaType::Enum { .. } | MetaType::Scalar { .. })
684    }
685
686    #[inline]
687    pub fn is_input(&self) -> bool {
688        matches!(
689            self,
690            MetaType::Enum { .. } | MetaType::Scalar { .. } | MetaType::InputObject { .. }
691        )
692    }
693
694    #[inline]
695    pub fn is_possible_type(&self, type_name: &str) -> bool {
696        match self {
697            MetaType::Interface { possible_types, .. } => possible_types.contains(type_name),
698            MetaType::Union { possible_types, .. } => possible_types.contains(type_name),
699            MetaType::Object { name, .. } => name == type_name,
700            _ => false,
701        }
702    }
703
704    #[inline]
705    pub fn possible_types(&self) -> Option<&IndexSet<String>> {
706        match self {
707            MetaType::Interface { possible_types, .. } => Some(possible_types),
708            MetaType::Union { possible_types, .. } => Some(possible_types),
709            _ => None,
710        }
711    }
712
713    pub fn type_overlap(&self, ty: &MetaType) -> bool {
714        if std::ptr::eq(self, ty) {
715            return true;
716        }
717
718        match (self.is_abstract(), ty.is_abstract()) {
719            (true, true) => self
720                .possible_types()
721                .iter()
722                .copied()
723                .flatten()
724                .any(|type_name| ty.is_possible_type(type_name)),
725            (true, false) => self.is_possible_type(ty.name()),
726            (false, true) => ty.is_possible_type(self.name()),
727            (false, false) => false,
728        }
729    }
730
731    pub fn rust_typename(&self) -> Option<&'static str> {
732        match self {
733            MetaType::Scalar { .. } => None,
734            MetaType::Object { rust_typename, .. } => *rust_typename,
735            MetaType::Interface { rust_typename, .. } => *rust_typename,
736            MetaType::Union { rust_typename, .. } => *rust_typename,
737            MetaType::Enum { rust_typename, .. } => *rust_typename,
738            MetaType::InputObject { rust_typename, .. } => *rust_typename,
739        }
740    }
741}
742
743pub struct MetaDirective {
744    pub name: String,
745    pub description: Option<String>,
746    pub locations: Vec<__DirectiveLocation>,
747    pub args: IndexMap<String, MetaInputValue>,
748    pub is_repeatable: bool,
749    pub visible: Option<MetaVisibleFn>,
750    pub composable: Option<String>,
751}
752
753impl MetaDirective {
754    pub(crate) fn sdl(&self, options: &SDLExportOptions) -> String {
755        let mut sdl = String::new();
756
757        if let Some(description) = &self.description {
758            self::export_sdl::write_description(&mut sdl, options, 0, description);
759        }
760
761        write!(sdl, "directive @{}", self.name).ok();
762
763        if !self.args.is_empty() {
764            let args = self
765                .args
766                .values()
767                .map(|value| self.argument_sdl(value))
768                .collect::<Vec<_>>()
769                .join(", ");
770            write!(sdl, "({})", args).ok();
771        }
772        let locations = self
773            .locations
774            .iter()
775            .map(|location| location.to_value().to_string())
776            .collect::<Vec<_>>()
777            .join(" | ");
778        write!(sdl, " on {}", locations).ok();
779        sdl
780    }
781
782    pub(crate) fn argument_sdl(&self, argument: &MetaInputValue) -> String {
783        let argument_default = match &argument.default_value {
784            Some(default) => format!(" = {default}"),
785            None => "".to_string(),
786        };
787
788        format!("{}: {}{}", argument.name, argument.ty, argument_default)
789    }
790}
791
792/// A type registry for build schemas
793#[derive(Default)]
794pub struct Registry {
795    pub types: BTreeMap<String, MetaType>,
796    pub directives: BTreeMap<String, MetaDirective>,
797    pub implements: HashMap<String, IndexSet<String>>,
798    pub query_type: String,
799    pub mutation_type: Option<String>,
800    pub subscription_type: Option<String>,
801    pub introspection_mode: IntrospectionMode,
802    pub enable_federation: bool,
803    pub federation_subscription: bool,
804    pub ignore_name_conflicts: HashSet<String>,
805    pub enable_suggestions: bool,
806}
807
808impl Registry {
809    pub(crate) fn add_system_types(&mut self) {
810        self.add_directive(MetaDirective {
811            name: "skip".into(),
812            description: Some("Directs the executor to skip this field or fragment when the `if` argument is true.".to_string()),
813            locations: vec![
814                __DirectiveLocation::FIELD,
815                __DirectiveLocation::FRAGMENT_SPREAD,
816                __DirectiveLocation::INLINE_FRAGMENT
817            ],
818            args: {
819                let mut args = IndexMap::new();
820                args.insert("if".to_string(), MetaInputValue {
821                    name: "if".to_string(),
822                    description: Some("Skipped when true.".to_string()),
823                    ty: "Boolean!".to_string(),
824                    deprecation: Deprecation::NoDeprecated,
825                    default_value: None,
826                    visible: None,
827                    inaccessible: false,
828                    tags: Default::default(),
829                    is_secret: false,
830                    directive_invocations: vec![]
831                });
832                args
833            },
834            is_repeatable: false,
835            visible: None,
836            composable: None,
837        });
838
839        self.add_directive(MetaDirective {
840            name: "include".into(),
841            description: Some("Directs the executor to include this field or fragment only when the `if` argument is true.".to_string()),
842            locations: vec![
843                __DirectiveLocation::FIELD,
844                __DirectiveLocation::FRAGMENT_SPREAD,
845                __DirectiveLocation::INLINE_FRAGMENT
846            ],
847            args: {
848                let mut args = IndexMap::new();
849                args.insert("if".to_string(), MetaInputValue {
850                    name: "if".to_string(),
851                    description: Some("Included when true.".to_string()),
852                    ty: "Boolean!".to_string(),
853                    deprecation: Deprecation::NoDeprecated,
854                    default_value: None,
855                    visible: None,
856                    inaccessible: false,
857                    tags: Default::default(),
858                    is_secret: false,
859                    directive_invocations: vec![]
860                });
861                args
862            },
863            is_repeatable: false,
864            visible: None,
865            composable: None,
866        });
867
868        self.add_directive(MetaDirective {
869            name: "deprecated".into(),
870            description: Some(
871                "Marks an element of a GraphQL schema as no longer supported.".into(),
872            ),
873            locations: vec![
874                __DirectiveLocation::FIELD_DEFINITION,
875                __DirectiveLocation::ARGUMENT_DEFINITION,
876                __DirectiveLocation::INPUT_FIELD_DEFINITION,
877                __DirectiveLocation::ENUM_VALUE,
878            ],
879            args: {
880                let mut args = IndexMap::new();
881                args.insert(
882                    "reason".into(),
883                    MetaInputValue {
884                        name: "reason".into(),
885                        description: Some(
886                            "A reason for why it is deprecated, formatted using Markdown syntax"
887                                .into(),
888                        ),
889                        ty: "String".into(),
890                        deprecation: Deprecation::NoDeprecated,
891                        default_value: Some(r#""No longer supported""#.into()),
892                        visible: None,
893                        inaccessible: false,
894                        tags: Default::default(),
895                        is_secret: false,
896                        directive_invocations: vec![],
897                    },
898                );
899                args
900            },
901            is_repeatable: false,
902            visible: None,
903            composable: None,
904        });
905
906        self.add_directive(MetaDirective {
907            name: "specifiedBy".into(),
908            description: Some("Provides a scalar specification URL for specifying the behavior of custom scalar types.".into()),
909            locations: vec![__DirectiveLocation::SCALAR],
910            args: {
911                let mut args = IndexMap::new();
912                args.insert(
913                    "url".into(),
914                    MetaInputValue {
915                        name: "url".into(),
916                        description: Some("URL that specifies the behavior of this scalar.".into()),
917                        ty: "String!".into(),
918                        deprecation: Deprecation::NoDeprecated,
919                        default_value: None,
920                        visible: None,
921                        inaccessible: false,
922                        tags: Default::default(),
923                        is_secret: false,
924                        directive_invocations: vec![],
925                    },
926                );
927                args
928            },
929            is_repeatable: false,
930            visible: None,
931            composable: None,
932        });
933
934        self.add_directive(MetaDirective {
935            name: "oneOf".into(),
936            description: Some(
937                "Indicates that an Input Object is a OneOf Input Object (and thus requires \
938                exactly one of its field be provided)"
939                    .to_string(),
940            ),
941            locations: vec![__DirectiveLocation::INPUT_OBJECT],
942            args: Default::default(),
943            is_repeatable: false,
944            visible: None,
945            composable: None,
946        });
947
948        // create system scalars
949        <bool as InputType>::create_type_info(self);
950        <i32 as InputType>::create_type_info(self);
951        <f32 as InputType>::create_type_info(self);
952        <String as InputType>::create_type_info(self);
953        <ID as InputType>::create_type_info(self);
954    }
955
956    pub fn create_input_type<T, F>(&mut self, type_id: MetaTypeId, mut f: F) -> String
957    where
958        T: InputType,
959        F: FnMut(&mut Registry) -> MetaType,
960    {
961        self.create_type(&mut f, &T::type_name(), std::any::type_name::<T>(), type_id);
962        T::qualified_type_name()
963    }
964
965    pub fn create_output_type<T, F>(&mut self, type_id: MetaTypeId, mut f: F) -> String
966    where
967        T: OutputType + ?Sized,
968        F: FnMut(&mut Registry) -> MetaType,
969    {
970        self.create_type(&mut f, &T::type_name(), std::any::type_name::<T>(), type_id);
971        T::qualified_type_name()
972    }
973
974    pub fn create_subscription_type<T, F>(&mut self, mut f: F) -> String
975    where
976        T: SubscriptionType + ?Sized,
977        F: FnMut(&mut Registry) -> MetaType,
978    {
979        self.create_type(
980            &mut f,
981            &T::type_name(),
982            std::any::type_name::<T>(),
983            MetaTypeId::Object,
984        );
985        T::qualified_type_name()
986    }
987
988    fn create_type<F: FnMut(&mut Registry) -> MetaType>(
989        &mut self,
990        f: &mut F,
991        name: &str,
992        rust_typename: &'static str,
993        type_id: MetaTypeId,
994    ) {
995        match self.types.get(name) {
996            Some(ty) => {
997                if let Some(prev_typename) = ty.rust_typename() {
998                    if prev_typename == "__fake_type__" {
999                        return;
1000                    }
1001
1002                    if rust_typename != prev_typename && !self.ignore_name_conflicts.contains(name)
1003                    {
1004                        panic!(
1005                            "`{}` and `{}` have the same GraphQL name `{}`",
1006                            prev_typename, rust_typename, name,
1007                        );
1008                    }
1009
1010                    if ty.type_id() != type_id {
1011                        panic!(
1012                            "Register `{}` as `{}`, but it is already registered as `{}`",
1013                            name,
1014                            type_id,
1015                            ty.type_id()
1016                        );
1017                    }
1018                }
1019            }
1020            None => {
1021                // Inserting a fake type before calling the function allows recursive types to
1022                // exist.
1023                self.types
1024                    .insert(name.to_string(), type_id.create_fake_type(rust_typename));
1025                let ty = f(self);
1026                *self.types.get_mut(name).unwrap() = ty;
1027            }
1028        }
1029    }
1030
1031    pub fn create_fake_output_type<T: OutputType>(&mut self) -> MetaType {
1032        T::create_type_info(self);
1033        self.types
1034            .get(&*T::type_name())
1035            .cloned()
1036            .expect("You definitely encountered a bug!")
1037    }
1038
1039    pub fn create_fake_input_type<T: InputType>(&mut self) -> MetaType {
1040        T::create_type_info(self);
1041        self.types
1042            .get(&*T::type_name())
1043            .cloned()
1044            .expect("You definitely encountered a bug!")
1045    }
1046
1047    pub fn create_fake_subscription_type<T: SubscriptionType>(&mut self) -> MetaType {
1048        T::create_type_info(self);
1049        self.types
1050            .get(&*T::type_name())
1051            .cloned()
1052            .expect("You definitely encountered a bug!")
1053    }
1054
1055    pub fn add_directive(&mut self, directive: MetaDirective) {
1056        self.directives
1057            .insert(directive.name.to_string(), directive);
1058    }
1059
1060    pub fn add_implements(&mut self, ty: &str, interface: &str) {
1061        self.implements
1062            .entry(ty.to_string())
1063            .and_modify(|interfaces| {
1064                interfaces.insert(interface.to_string());
1065            })
1066            .or_insert({
1067                let mut interfaces = IndexSet::new();
1068                interfaces.insert(interface.to_string());
1069                interfaces
1070            });
1071    }
1072
1073    pub fn add_keys(&mut self, ty: &str, keys: impl Into<String>) {
1074        let all_keys = match self.types.get_mut(ty) {
1075            Some(MetaType::Object { keys: all_keys, .. }) => all_keys,
1076            Some(MetaType::Interface { keys: all_keys, .. }) => all_keys,
1077            _ => return,
1078        };
1079        if let Some(all_keys) = all_keys {
1080            all_keys.push(keys.into());
1081        } else {
1082            *all_keys = Some(vec![keys.into()]);
1083        }
1084    }
1085
1086    pub fn concrete_type_by_name(&self, type_name: &str) -> Option<&MetaType> {
1087        self.types.get(MetaTypeName::concrete_typename(type_name))
1088    }
1089
1090    pub fn concrete_type_by_parsed_type(&self, query_type: &ParsedType) -> Option<&MetaType> {
1091        match &query_type.base {
1092            ParsedBaseType::Named(name) => self.types.get(name.as_str()),
1093            ParsedBaseType::List(ty) => self.concrete_type_by_parsed_type(ty),
1094        }
1095    }
1096
1097    pub(crate) fn has_entities(&self) -> bool {
1098        self.types.values().any(|ty| match ty {
1099            MetaType::Object {
1100                keys: Some(keys),
1101                resolvable: true,
1102                ..
1103            }
1104            | MetaType::Interface {
1105                keys: Some(keys), ..
1106            } => !keys.is_empty(),
1107            _ => false,
1108        })
1109    }
1110
1111    /// Each type annotated with @key should be added to the _Entity union.
1112    /// If no types are annotated with the key directive, then the _Entity union
1113    /// and Query._entities field should be removed from the schema.
1114    ///
1115    /// [Reference](https://www.apollographql.com/docs/federation/federation-spec/#resolve-requests-for-entities).
1116    fn create_entity_type_and_root_field(&mut self) {
1117        let possible_types: IndexSet<String> = self
1118            .types
1119            .values()
1120            .filter_map(|ty| match ty {
1121                MetaType::Object {
1122                    name,
1123                    keys: Some(keys),
1124                    resolvable: true,
1125                    ..
1126                } if !keys.is_empty() => Some(name.clone()),
1127                MetaType::Interface {
1128                    name,
1129                    keys: Some(keys),
1130                    ..
1131                } if !keys.is_empty() => Some(name.clone()),
1132                _ => None,
1133            })
1134            .collect();
1135
1136        if let MetaType::Object { fields, .. } = self
1137            .types
1138            .get_mut(&self.query_type)
1139            .expect("missing query type")
1140        {
1141            fields.insert(
1142                "_service".to_string(),
1143                MetaField {
1144                    name: "_service".to_string(),
1145                    description: None,
1146                    args: Default::default(),
1147                    ty: "_Service!".to_string(),
1148                    deprecation: Default::default(),
1149                    cache_control: Default::default(),
1150                    external: false,
1151                    requires: None,
1152                    provides: None,
1153                    shareable: false,
1154                    inaccessible: false,
1155                    tags: Default::default(),
1156                    override_from: None,
1157                    visible: None,
1158                    compute_complexity: None,
1159                    directive_invocations: vec![],
1160                    requires_scopes: vec![],
1161                },
1162            );
1163        }
1164
1165        if !possible_types.is_empty() {
1166            self.types.insert(
1167                "_Entity".to_string(),
1168                MetaType::Union {
1169                    name: "_Entity".to_string(),
1170                    description: None,
1171                    possible_types,
1172                    visible: None,
1173                    inaccessible: false,
1174                    tags: Default::default(),
1175                    rust_typename: Some("async_graphql::federation::Entity"),
1176                    directive_invocations: vec![],
1177                },
1178            );
1179
1180            if let MetaType::Object { fields, .. } = self.types.get_mut(&self.query_type).unwrap() {
1181                fields.insert(
1182                    "_entities".to_string(),
1183                    MetaField {
1184                        name: "_entities".to_string(),
1185                        description: None,
1186                        args: {
1187                            let mut args = IndexMap::new();
1188                            args.insert(
1189                                "representations".to_string(),
1190                                MetaInputValue {
1191                                    name: "representations".to_string(),
1192                                    description: None,
1193                                    ty: "[_Any!]!".to_string(),
1194                                    deprecation: Deprecation::NoDeprecated,
1195                                    default_value: None,
1196                                    visible: None,
1197                                    inaccessible: false,
1198                                    tags: Default::default(),
1199                                    is_secret: false,
1200                                    directive_invocations: vec![],
1201                                },
1202                            );
1203                            args
1204                        },
1205                        ty: "[_Entity]!".to_string(),
1206                        deprecation: Default::default(),
1207                        cache_control: Default::default(),
1208                        external: false,
1209                        requires: None,
1210                        provides: None,
1211                        shareable: false,
1212                        visible: None,
1213                        inaccessible: false,
1214                        tags: Default::default(),
1215                        override_from: None,
1216                        compute_complexity: None,
1217                        directive_invocations: vec![],
1218                        requires_scopes: vec![],
1219                    },
1220                );
1221            }
1222        }
1223    }
1224
1225    pub(crate) fn create_introspection_types(&mut self) {
1226        __Schema::create_type_info(self);
1227
1228        if let Some(MetaType::Object { fields, .. }) = self.types.get_mut(&self.query_type) {
1229            fields.insert(
1230                "__schema".to_string(),
1231                MetaField {
1232                    name: "__schema".to_string(),
1233                    description: Some("Access the current type schema of this server.".to_string()),
1234                    args: Default::default(),
1235                    ty: "__Schema".to_string(),
1236                    deprecation: Default::default(),
1237                    cache_control: Default::default(),
1238                    external: false,
1239                    requires: None,
1240                    provides: None,
1241                    shareable: false,
1242                    inaccessible: false,
1243                    tags: Default::default(),
1244                    visible: None,
1245                    compute_complexity: None,
1246                    override_from: None,
1247                    directive_invocations: vec![],
1248                    requires_scopes: vec![],
1249                },
1250            );
1251
1252            fields.insert(
1253                "__type".to_string(),
1254                MetaField {
1255                    name: "__type".to_string(),
1256                    description: Some("Request the type information of a single type.".to_string()),
1257                    args: {
1258                        let mut args = IndexMap::new();
1259                        args.insert(
1260                            "name".to_string(),
1261                            MetaInputValue {
1262                                name: "name".to_string(),
1263                                description: None,
1264                                ty: "String!".to_string(),
1265                                deprecation: Deprecation::NoDeprecated,
1266                                default_value: None,
1267                                visible: None,
1268                                inaccessible: false,
1269                                tags: Default::default(),
1270                                is_secret: false,
1271                                directive_invocations: vec![],
1272                            },
1273                        );
1274                        args
1275                    },
1276                    ty: "__Type".to_string(),
1277                    deprecation: Default::default(),
1278                    cache_control: Default::default(),
1279                    external: false,
1280                    requires: None,
1281                    provides: None,
1282                    shareable: false,
1283                    inaccessible: false,
1284                    tags: Default::default(),
1285                    override_from: None,
1286                    visible: None,
1287                    compute_complexity: None,
1288                    directive_invocations: vec![],
1289                    requires_scopes: vec![],
1290                },
1291            );
1292        }
1293    }
1294
1295    pub(crate) fn create_federation_types(&mut self) {
1296        <Any as InputType>::create_type_info(self);
1297
1298        self.types.insert(
1299            "_Service".to_string(),
1300            MetaType::Object {
1301                name: "_Service".to_string(),
1302                description: None,
1303                fields: {
1304                    let mut fields = IndexMap::new();
1305                    fields.insert(
1306                        "sdl".to_string(),
1307                        MetaField {
1308                            name: "sdl".to_string(),
1309                            description: None,
1310                            args: Default::default(),
1311                            ty: "String".to_string(),
1312                            deprecation: Default::default(),
1313                            cache_control: Default::default(),
1314                            external: false,
1315                            requires: None,
1316                            provides: None,
1317                            shareable: false,
1318                            visible: None,
1319                            inaccessible: false,
1320                            tags: Default::default(),
1321                            override_from: None,
1322                            compute_complexity: None,
1323                            directive_invocations: vec![],
1324                            requires_scopes: vec![],
1325                        },
1326                    );
1327                    fields
1328                },
1329                cache_control: Default::default(),
1330                extends: false,
1331                shareable: false,
1332                resolvable: true,
1333                interface_object: false,
1334                keys: None,
1335                visible: None,
1336                inaccessible: false,
1337                tags: Default::default(),
1338                is_subscription: false,
1339                rust_typename: Some("async_graphql::federation::Service"),
1340                directive_invocations: vec![],
1341                requires_scopes: vec![],
1342            },
1343        );
1344
1345        self.create_entity_type_and_root_field();
1346    }
1347
1348    pub fn names(&self) -> Vec<String> {
1349        let mut names = HashSet::new();
1350
1351        for d in self.directives.values() {
1352            names.insert(d.name.to_string());
1353            names.extend(d.args.values().map(|arg| arg.name.to_string()));
1354        }
1355
1356        for ty in self.types.values() {
1357            match ty {
1358                MetaType::Scalar { name, .. } | MetaType::Union { name, .. } => {
1359                    names.insert(name.clone());
1360                }
1361                MetaType::Object { name, fields, .. }
1362                | MetaType::Interface { name, fields, .. } => {
1363                    names.insert(name.clone());
1364                    names.extend(
1365                        fields
1366                            .values()
1367                            .map(|field| {
1368                                std::iter::once(field.name.clone())
1369                                    .chain(field.args.values().map(|arg| arg.name.to_string()))
1370                            })
1371                            .flatten(),
1372                    );
1373                }
1374                MetaType::Enum {
1375                    name, enum_values, ..
1376                } => {
1377                    names.insert(name.clone());
1378                    names.extend(enum_values.values().map(|value| value.name.to_string()));
1379                }
1380                MetaType::InputObject {
1381                    name, input_fields, ..
1382                } => {
1383                    names.insert(name.clone());
1384                    names.extend(input_fields.values().map(|field| field.name.to_string()));
1385                }
1386            }
1387        }
1388
1389        names.into_iter().collect()
1390    }
1391
1392    pub fn set_description(&mut self, name: impl AsRef<str>, desc: impl Into<String>) {
1393        let desc = desc.into();
1394        match self.types.get_mut(name.as_ref()) {
1395            Some(MetaType::Scalar { description, .. }) => *description = Some(desc),
1396            Some(MetaType::Object { description, .. }) => *description = Some(desc),
1397            Some(MetaType::Interface { description, .. }) => *description = Some(desc),
1398            Some(MetaType::Union { description, .. }) => *description = Some(desc),
1399            Some(MetaType::Enum { description, .. }) => *description = Some(desc),
1400            Some(MetaType::InputObject { description, .. }) => *description = Some(desc),
1401            None => {}
1402        }
1403    }
1404
1405    pub fn remove_unused_types(&mut self) {
1406        let mut used_types = BTreeSet::new();
1407        let mut unused_types = BTreeSet::new();
1408
1409        fn traverse_field<'a>(
1410            types: &'a BTreeMap<String, MetaType>,
1411            used_types: &mut BTreeSet<&'a str>,
1412            field: &'a MetaField,
1413        ) {
1414            traverse_type(
1415                types,
1416                used_types,
1417                MetaTypeName::concrete_typename(&field.ty),
1418            );
1419            for arg in field.args.values() {
1420                traverse_input_value(types, used_types, arg);
1421            }
1422        }
1423
1424        fn traverse_input_value<'a>(
1425            types: &'a BTreeMap<String, MetaType>,
1426            used_types: &mut BTreeSet<&'a str>,
1427            input_value: &'a MetaInputValue,
1428        ) {
1429            traverse_type(
1430                types,
1431                used_types,
1432                MetaTypeName::concrete_typename(&input_value.ty),
1433            );
1434        }
1435
1436        fn traverse_type<'a>(
1437            types: &'a BTreeMap<String, MetaType>,
1438            used_types: &mut BTreeSet<&'a str>,
1439            type_name: &'a str,
1440        ) {
1441            if used_types.contains(type_name) {
1442                return;
1443            }
1444
1445            if let Some(ty) = types.get(type_name) {
1446                used_types.insert(type_name);
1447                match ty {
1448                    MetaType::Object { fields, .. } => {
1449                        for field in fields.values() {
1450                            traverse_field(types, used_types, field);
1451                        }
1452                    }
1453                    MetaType::Interface {
1454                        fields,
1455                        possible_types,
1456                        ..
1457                    } => {
1458                        for field in fields.values() {
1459                            traverse_field(types, used_types, field);
1460                        }
1461                        for type_name in possible_types.iter() {
1462                            traverse_type(types, used_types, type_name);
1463                        }
1464                    }
1465                    MetaType::Union { possible_types, .. } => {
1466                        for type_name in possible_types.iter() {
1467                            traverse_type(types, used_types, type_name);
1468                        }
1469                    }
1470                    MetaType::InputObject { input_fields, .. } => {
1471                        for field in input_fields.values() {
1472                            traverse_input_value(types, used_types, field);
1473                        }
1474                    }
1475                    _ => {}
1476                }
1477            }
1478        }
1479
1480        for directive in self.directives.values() {
1481            for arg in directive.args.values() {
1482                traverse_input_value(&self.types, &mut used_types, arg);
1483            }
1484        }
1485
1486        for type_name in Some(&self.query_type)
1487            .into_iter()
1488            .chain(self.mutation_type.iter())
1489            .chain(self.subscription_type.iter())
1490        {
1491            traverse_type(&self.types, &mut used_types, type_name);
1492        }
1493
1494        for ty in self.types.values().filter(|ty| match ty {
1495            MetaType::Object {
1496                keys: Some(keys), ..
1497            }
1498            | MetaType::Interface {
1499                keys: Some(keys), ..
1500            } => !keys.is_empty(),
1501            _ => false,
1502        }) {
1503            traverse_type(&self.types, &mut used_types, ty.name());
1504        }
1505
1506        for ty in self.types.values() {
1507            let name = ty.name();
1508            if !is_system_type(name) && !used_types.contains(name) {
1509                unused_types.insert(name.to_string());
1510            }
1511        }
1512
1513        for type_name in unused_types {
1514            self.types.remove(&type_name);
1515        }
1516    }
1517
1518    pub fn find_visible_types(&self, ctx: &Context<'_>) -> HashSet<&str> {
1519        let mut visible_types = HashSet::new();
1520
1521        fn traverse_field<'a>(
1522            ctx: &Context<'_>,
1523            types: &'a BTreeMap<String, MetaType>,
1524            visible_types: &mut HashSet<&'a str>,
1525            field: &'a MetaField,
1526        ) {
1527            if !is_visible(ctx, &field.visible) {
1528                return;
1529            }
1530
1531            traverse_type(
1532                ctx,
1533                types,
1534                visible_types,
1535                MetaTypeName::concrete_typename(&field.ty),
1536            );
1537            for arg in field.args.values() {
1538                traverse_input_value(ctx, types, visible_types, arg);
1539            }
1540        }
1541
1542        fn traverse_input_value<'a>(
1543            ctx: &Context<'_>,
1544            types: &'a BTreeMap<String, MetaType>,
1545            visible_types: &mut HashSet<&'a str>,
1546            input_value: &'a MetaInputValue,
1547        ) {
1548            if !is_visible(ctx, &input_value.visible) {
1549                return;
1550            }
1551
1552            traverse_type(
1553                ctx,
1554                types,
1555                visible_types,
1556                MetaTypeName::concrete_typename(&input_value.ty),
1557            );
1558        }
1559
1560        fn traverse_type<'a>(
1561            ctx: &Context<'_>,
1562            types: &'a BTreeMap<String, MetaType>,
1563            visible_types: &mut HashSet<&'a str>,
1564            type_name: &'a str,
1565        ) {
1566            if visible_types.contains(type_name) {
1567                return;
1568            }
1569
1570            if let Some(ty) = types.get(type_name) {
1571                if !ty.is_visible(ctx) {
1572                    return;
1573                }
1574
1575                visible_types.insert(type_name);
1576                match ty {
1577                    MetaType::Object { fields, .. } => {
1578                        for field in fields.values() {
1579                            traverse_field(ctx, types, visible_types, field);
1580                        }
1581                    }
1582                    MetaType::Interface {
1583                        fields,
1584                        possible_types,
1585                        ..
1586                    } => {
1587                        for field in fields.values() {
1588                            traverse_field(ctx, types, visible_types, field);
1589                        }
1590                        for type_name in possible_types.iter() {
1591                            traverse_type(ctx, types, visible_types, type_name);
1592                        }
1593                    }
1594                    MetaType::Union { possible_types, .. } => {
1595                        for type_name in possible_types.iter() {
1596                            traverse_type(ctx, types, visible_types, type_name);
1597                        }
1598                    }
1599                    MetaType::InputObject { input_fields, .. } => {
1600                        for field in input_fields.values() {
1601                            traverse_input_value(ctx, types, visible_types, field);
1602                        }
1603                    }
1604                    _ => {}
1605                }
1606            }
1607        }
1608
1609        for directive in self.directives.values() {
1610            if is_visible(ctx, &directive.visible) {
1611                for arg in directive.args.values() {
1612                    traverse_input_value(ctx, &self.types, &mut visible_types, arg);
1613                }
1614            }
1615        }
1616
1617        for type_name in Some(&self.query_type)
1618            .into_iter()
1619            .chain(self.mutation_type.iter())
1620            .chain(self.subscription_type.iter())
1621        {
1622            traverse_type(ctx, &self.types, &mut visible_types, type_name);
1623        }
1624
1625        for ty in self.types.values().filter(|ty| match ty {
1626            MetaType::Object {
1627                keys: Some(keys), ..
1628            }
1629            | MetaType::Interface {
1630                keys: Some(keys), ..
1631            } => !keys.is_empty(),
1632            _ => false,
1633        }) {
1634            traverse_type(ctx, &self.types, &mut visible_types, ty.name());
1635        }
1636
1637        for ty in self.types.values() {
1638            if let MetaType::Interface { possible_types, .. } = ty {
1639                if ty.is_visible(ctx) && !visible_types.contains(ty.name()) {
1640                    for type_name in possible_types.iter() {
1641                        if visible_types.contains(type_name.as_str()) {
1642                            traverse_type(ctx, &self.types, &mut visible_types, ty.name());
1643                            break;
1644                        }
1645                    }
1646                }
1647            }
1648        }
1649
1650        self.types
1651            .values()
1652            .filter_map(|ty| {
1653                let name = ty.name();
1654                if is_system_type(name) || visible_types.contains(name) {
1655                    Some(name)
1656                } else {
1657                    None
1658                }
1659            })
1660            .collect()
1661    }
1662}
1663
1664pub(crate) fn is_visible(ctx: &Context<'_>, visible: &Option<MetaVisibleFn>) -> bool {
1665    match visible {
1666        Some(f) => f(ctx),
1667        None => true,
1668    }
1669}
1670
1671fn is_system_type(name: &str) -> bool {
1672    if name.starts_with("__") {
1673        return true;
1674    }
1675
1676    name == "Boolean" || name == "Int" || name == "Float" || name == "String" || name == "ID"
1677}
1678
1679#[cfg(test)]
1680mod test {
1681    use crate::registry::MetaDirectiveInvocation;
1682
1683    #[test]
1684    fn test_directive_invocation_dsl() {
1685        let expected = r#"@testDirective(int_value: 1, str_value: "abc")"#;
1686        assert_eq!(
1687            expected.to_string(),
1688            MetaDirectiveInvocation {
1689                name: "testDirective".to_string(),
1690                args: [
1691                    ("int_value".to_string(), 1u32.into()),
1692                    ("str_value".to_string(), "abc".into())
1693                ]
1694                .into(),
1695            }
1696            .sdl()
1697        )
1698    }
1699}