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#[derive(Debug, Clone)]
111pub struct MetaDirectiveInvocation {
112 pub name: String,
114 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#[derive(Clone)]
138pub struct MetaInputValue {
139 pub name: String,
141 pub description: Option<String>,
143 pub ty: String,
145 pub deprecation: Deprecation,
147 pub default_value: Option<String>,
149 pub visible: Option<MetaVisibleFn>,
152 pub inaccessible: bool,
155 pub tags: Vec<String>,
158 pub is_secret: bool,
160 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#[derive(Clone)]
197pub struct MetaField {
198 pub name: String,
200 pub description: Option<String>,
202 pub args: IndexMap<String, MetaInputValue>,
204 pub ty: String,
206 pub deprecation: Deprecation,
208 pub cache_control: CacheControl,
210 pub external: bool,
214 pub requires: Option<String>,
219 pub provides: Option<String>,
222 pub visible: Option<MetaVisibleFn>,
225 pub shareable: bool,
228 pub inaccessible: bool,
231 pub tags: Vec<String>,
234 pub override_from: Option<String>,
237 pub compute_complexity: Option<ComputeComplexityFn>,
239 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
357pub type ScalarValidatorFn = Arc<dyn Fn(&Value) -> bool + Send + Sync>;
359
360#[derive(Clone)]
362pub enum MetaType {
363 Scalar {
367 name: String,
369 description: Option<String>,
371 is_valid: Option<ScalarValidatorFn>,
373 visible: Option<MetaVisibleFn>,
376 inaccessible: bool,
381 tags: Vec<String>,
386 specified_by_url: Option<String>,
390 directive_invocations: Vec<MetaDirectiveInvocation>,
392 },
393 Object {
397 name: String,
399 description: Option<String>,
401 fields: IndexMap<String, MetaField>,
403 cache_control: CacheControl,
405 extends: bool,
410 shareable: bool,
416 resolvable: bool,
424 keys: Option<Vec<String>>,
432 visible: Option<MetaVisibleFn>,
435 inaccessible: bool,
440 interface_object: bool,
446 tags: Vec<String>,
451 is_subscription: bool,
453 rust_typename: Option<&'static str>,
455 directive_invocations: Vec<MetaDirectiveInvocation>,
457 },
458 Interface {
462 name: String,
464 description: Option<String>,
466 fields: IndexMap<String, MetaField>,
468 possible_types: IndexSet<String>,
471 extends: bool,
476 keys: Option<Vec<String>>,
484 visible: Option<MetaVisibleFn>,
487 inaccessible: bool,
492 tags: Vec<String>,
497 rust_typename: Option<&'static str>,
499 directive_invocations: Vec<MetaDirectiveInvocation>,
501 },
502 Union {
506 name: String,
508 description: Option<String>,
510 possible_types: IndexSet<String>,
512 visible: Option<MetaVisibleFn>,
515 inaccessible: bool,
520 tags: Vec<String>,
525 rust_typename: Option<&'static str>,
527 directive_invocations: Vec<MetaDirectiveInvocation>,
529 },
530 Enum {
534 name: String,
536 description: Option<String>,
538 enum_values: IndexMap<String, MetaEnumValue>,
540 visible: Option<MetaVisibleFn>,
543 inaccessible: bool,
548 tags: Vec<String>,
553 rust_typename: Option<&'static str>,
555 directive_invocations: Vec<MetaDirectiveInvocation>,
557 },
558 InputObject {
562 name: String,
564 description: Option<String>,
566 input_fields: IndexMap<String, MetaInputValue>,
568 visible: Option<MetaVisibleFn>,
571 inaccessible: bool,
576 tags: Vec<String>,
581 rust_typename: Option<&'static str>,
583 oneof: bool,
587 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#[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 <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 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 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}