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