1mod cache_control;
2mod export_sdl;
3mod stringify_exec_doc;
4
5use std::{
6 collections::{BTreeMap, BTreeSet, HashMap, HashSet},
7 fmt::{self, Display, Formatter, Write},
8 sync::Arc,
9};
10
11pub use cache_control::CacheControl;
12pub use export_sdl::SDLExportOptions;
13use indexmap::{map::IndexMap, set::IndexSet};
14
15pub use crate::model::{__DirectiveLocation, location_traits};
16use crate::{
17 Any, Context, ID, InputType, OutputType, Positioned, ServerResult, SubscriptionType, Value,
18 VisitorContext,
19 model::__Schema,
20 parser::types::{BaseType as ParsedBaseType, Field, Type as ParsedType, VariableDefinition},
21 schema::IntrospectionMode,
22};
23
24fn strip_brackets(type_name: &str) -> Option<&str> {
25 type_name
26 .strip_prefix('[')
27 .map(|rest| &rest[..rest.len() - 1])
28}
29
30#[derive(Clone, Copy, Eq, PartialEq, Debug)]
31pub enum MetaTypeName<'a> {
32 List(&'a str),
33 NonNull(&'a str),
34 Named(&'a str),
35}
36
37impl Display for MetaTypeName<'_> {
38 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
39 match self {
40 MetaTypeName::Named(name) => write!(f, "{}", name),
41 MetaTypeName::NonNull(name) => write!(f, "{}!", name),
42 MetaTypeName::List(name) => write!(f, "[{}]", name),
43 }
44 }
45}
46
47impl MetaTypeName<'_> {
48 #[inline]
49 pub fn create(type_name: &str) -> MetaTypeName<'_> {
50 if let Some(type_name) = type_name.strip_suffix('!') {
51 MetaTypeName::NonNull(type_name)
52 } else if let Some(type_name) = strip_brackets(type_name) {
53 MetaTypeName::List(type_name)
54 } else {
55 MetaTypeName::Named(type_name)
56 }
57 }
58
59 #[inline]
60 pub fn concrete_typename(type_name: &str) -> &str {
61 match MetaTypeName::create(type_name) {
62 MetaTypeName::List(type_name) => Self::concrete_typename(type_name),
63 MetaTypeName::NonNull(type_name) => Self::concrete_typename(type_name),
64 MetaTypeName::Named(type_name) => type_name,
65 }
66 }
67
68 #[inline]
69 pub fn is_non_null(&self) -> bool {
70 matches!(self, MetaTypeName::NonNull(_))
71 }
72
73 #[inline]
74 #[must_use]
75 pub fn unwrap_non_null(&self) -> Self {
76 match self {
77 MetaTypeName::NonNull(ty) => MetaTypeName::create(ty),
78 _ => *self,
79 }
80 }
81
82 #[inline]
83 pub fn is_subtype(&self, sub: &MetaTypeName<'_>) -> bool {
84 match (self, sub) {
85 (MetaTypeName::NonNull(super_type), MetaTypeName::NonNull(sub_type))
86 | (MetaTypeName::Named(super_type), MetaTypeName::NonNull(sub_type)) => {
87 MetaTypeName::create(super_type).is_subtype(&MetaTypeName::create(sub_type))
88 }
89 (MetaTypeName::Named(super_type), MetaTypeName::Named(sub_type)) => {
90 super_type == sub_type
91 }
92 (MetaTypeName::List(super_type), MetaTypeName::List(sub_type)) => {
93 MetaTypeName::create(super_type).is_subtype(&MetaTypeName::create(sub_type))
94 }
95 _ => false,
96 }
97 }
98
99 #[inline]
100 pub fn is_list(&self) -> bool {
101 match self {
102 MetaTypeName::List(_) => true,
103 MetaTypeName::NonNull(ty) => MetaTypeName::create(ty).is_list(),
104 MetaTypeName::Named(name) => name.ends_with(']'),
105 }
106 }
107}
108
109#[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 pub requires_scopes: Vec<String>,
245}
246
247#[derive(Clone)]
248pub struct MetaEnumValue {
249 pub name: String,
250 pub description: Option<String>,
251 pub deprecation: Deprecation,
252 pub visible: Option<MetaVisibleFn>,
253 pub inaccessible: bool,
254 pub tags: Vec<String>,
255 pub directive_invocations: Vec<MetaDirectiveInvocation>,
256}
257
258type MetaVisibleFn = fn(&Context<'_>) -> bool;
259
260#[derive(Debug, Copy, Clone, Eq, PartialEq)]
261pub enum MetaTypeId {
262 Scalar,
263 Object,
264 Interface,
265 Union,
266 Enum,
267 InputObject,
268}
269
270impl MetaTypeId {
271 fn create_fake_type(&self, rust_typename: &'static str) -> MetaType {
272 match self {
273 MetaTypeId::Scalar => MetaType::Scalar {
274 name: "".to_string(),
275 description: None,
276 is_valid: None,
277 visible: None,
278 inaccessible: false,
279 tags: vec![],
280 specified_by_url: None,
281 directive_invocations: vec![],
282 requires_scopes: vec![],
283 },
284 MetaTypeId::Object => MetaType::Object {
285 name: "".to_string(),
286 description: None,
287 fields: Default::default(),
288 cache_control: Default::default(),
289 extends: false,
290 shareable: false,
291 resolvable: true,
292 inaccessible: false,
293 interface_object: false,
294 tags: vec![],
295 keys: None,
296 visible: None,
297 is_subscription: false,
298 rust_typename: Some(rust_typename),
299 directive_invocations: vec![],
300 requires_scopes: vec![],
301 },
302 MetaTypeId::Interface => MetaType::Interface {
303 name: "".to_string(),
304 description: None,
305 fields: Default::default(),
306 possible_types: Default::default(),
307 extends: false,
308 inaccessible: false,
309 tags: vec![],
310 keys: None,
311 visible: None,
312 rust_typename: Some(rust_typename),
313 directive_invocations: vec![],
314 requires_scopes: vec![],
315 },
316 MetaTypeId::Union => MetaType::Union {
317 name: "".to_string(),
318 description: None,
319 possible_types: Default::default(),
320 visible: None,
321 inaccessible: false,
322 tags: vec![],
323 rust_typename: Some(rust_typename),
324 directive_invocations: vec![],
325 },
326 MetaTypeId::Enum => MetaType::Enum {
327 name: "".to_string(),
328 description: None,
329 enum_values: Default::default(),
330 visible: None,
331 inaccessible: false,
332 tags: vec![],
333 rust_typename: Some(rust_typename),
334 directive_invocations: vec![],
335 requires_scopes: vec![],
336 },
337 MetaTypeId::InputObject => MetaType::InputObject {
338 name: "".to_string(),
339 description: None,
340 input_fields: Default::default(),
341 visible: None,
342 inaccessible: false,
343 tags: vec![],
344 rust_typename: Some(rust_typename),
345 oneof: false,
346 directive_invocations: vec![],
347 },
348 }
349 }
350}
351
352impl Display for MetaTypeId {
353 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
354 f.write_str(match self {
355 MetaTypeId::Scalar => "Scalar",
356 MetaTypeId::Object => "Object",
357 MetaTypeId::Interface => "Interface",
358 MetaTypeId::Union => "Union",
359 MetaTypeId::Enum => "Enum",
360 MetaTypeId::InputObject => "InputObject",
361 })
362 }
363}
364
365pub type ScalarValidatorFn = Arc<dyn Fn(&Value) -> bool + Send + Sync>;
367
368#[derive(Clone)]
370pub enum MetaType {
371 Scalar {
375 name: String,
377 description: Option<String>,
379 is_valid: Option<ScalarValidatorFn>,
381 visible: Option<MetaVisibleFn>,
384 inaccessible: bool,
389 tags: Vec<String>,
394 specified_by_url: Option<String>,
398 directive_invocations: Vec<MetaDirectiveInvocation>,
400 requires_scopes: Vec<String>,
404 },
405 Object {
409 name: String,
411 description: Option<String>,
413 fields: IndexMap<String, MetaField>,
415 cache_control: CacheControl,
417 extends: bool,
422 shareable: bool,
428 resolvable: bool,
436 keys: Option<Vec<String>>,
444 visible: Option<MetaVisibleFn>,
447 inaccessible: bool,
452 interface_object: bool,
458 tags: Vec<String>,
463 is_subscription: bool,
465 rust_typename: Option<&'static str>,
467 directive_invocations: Vec<MetaDirectiveInvocation>,
469 requires_scopes: Vec<String>,
473 },
474 Interface {
478 name: String,
480 description: Option<String>,
482 fields: IndexMap<String, MetaField>,
484 possible_types: IndexSet<String>,
487 extends: bool,
492 keys: Option<Vec<String>>,
500 visible: Option<MetaVisibleFn>,
503 inaccessible: bool,
508 tags: Vec<String>,
513 rust_typename: Option<&'static str>,
515 directive_invocations: Vec<MetaDirectiveInvocation>,
517 requires_scopes: Vec<String>,
521 },
522 Union {
526 name: String,
528 description: Option<String>,
530 possible_types: IndexSet<String>,
532 visible: Option<MetaVisibleFn>,
535 inaccessible: bool,
540 tags: Vec<String>,
545 rust_typename: Option<&'static str>,
547 directive_invocations: Vec<MetaDirectiveInvocation>,
549 },
550 Enum {
554 name: String,
556 description: Option<String>,
558 enum_values: IndexMap<String, MetaEnumValue>,
560 visible: Option<MetaVisibleFn>,
563 inaccessible: bool,
568 tags: Vec<String>,
573 rust_typename: Option<&'static str>,
575 directive_invocations: Vec<MetaDirectiveInvocation>,
577 requires_scopes: Vec<String>,
581 },
582 InputObject {
586 name: String,
588 description: Option<String>,
590 input_fields: IndexMap<String, MetaInputValue>,
592 visible: Option<MetaVisibleFn>,
595 inaccessible: bool,
600 tags: Vec<String>,
605 rust_typename: Option<&'static str>,
607 oneof: bool,
611 directive_invocations: Vec<MetaDirectiveInvocation>,
613 },
614}
615
616impl MetaType {
617 #[inline]
618 pub fn type_id(&self) -> MetaTypeId {
619 match self {
620 MetaType::Scalar { .. } => MetaTypeId::Scalar,
621 MetaType::Object { .. } => MetaTypeId::Object,
622 MetaType::Interface { .. } => MetaTypeId::Interface,
623 MetaType::Union { .. } => MetaTypeId::Union,
624 MetaType::Enum { .. } => MetaTypeId::Enum,
625 MetaType::InputObject { .. } => MetaTypeId::InputObject,
626 }
627 }
628
629 #[inline]
630 pub fn field_by_name(&self, name: &str) -> Option<&MetaField> {
631 self.fields().and_then(|fields| fields.get(name))
632 }
633
634 #[inline]
635 pub fn fields(&self) -> Option<&IndexMap<String, MetaField>> {
636 match self {
637 MetaType::Object { fields, .. } => Some(&fields),
638 MetaType::Interface { fields, .. } => Some(&fields),
639 _ => None,
640 }
641 }
642
643 #[inline]
644 pub fn is_visible(&self, ctx: &Context<'_>) -> bool {
645 let visible = match self {
646 MetaType::Scalar { visible, .. } => visible,
647 MetaType::Object { visible, .. } => visible,
648 MetaType::Interface { visible, .. } => visible,
649 MetaType::Union { visible, .. } => visible,
650 MetaType::Enum { visible, .. } => visible,
651 MetaType::InputObject { visible, .. } => visible,
652 };
653 is_visible(ctx, visible)
654 }
655
656 #[inline]
657 pub fn name(&self) -> &str {
658 match self {
659 MetaType::Scalar { name, .. } => &name,
660 MetaType::Object { name, .. } => name,
661 MetaType::Interface { name, .. } => name,
662 MetaType::Union { name, .. } => name,
663 MetaType::Enum { name, .. } => name,
664 MetaType::InputObject { name, .. } => name,
665 }
666 }
667
668 #[inline]
669 pub fn is_composite(&self) -> bool {
670 matches!(
671 self,
672 MetaType::Object { .. } | MetaType::Interface { .. } | MetaType::Union { .. }
673 )
674 }
675
676 #[inline]
677 pub fn is_abstract(&self) -> bool {
678 matches!(self, MetaType::Interface { .. } | MetaType::Union { .. })
679 }
680
681 #[inline]
682 pub fn is_leaf(&self) -> bool {
683 matches!(self, MetaType::Enum { .. } | MetaType::Scalar { .. })
684 }
685
686 #[inline]
687 pub fn is_input(&self) -> bool {
688 matches!(
689 self,
690 MetaType::Enum { .. } | MetaType::Scalar { .. } | MetaType::InputObject { .. }
691 )
692 }
693
694 #[inline]
695 pub fn is_possible_type(&self, type_name: &str) -> bool {
696 match self {
697 MetaType::Interface { possible_types, .. } => possible_types.contains(type_name),
698 MetaType::Union { possible_types, .. } => possible_types.contains(type_name),
699 MetaType::Object { name, .. } => name == type_name,
700 _ => false,
701 }
702 }
703
704 #[inline]
705 pub fn possible_types(&self) -> Option<&IndexSet<String>> {
706 match self {
707 MetaType::Interface { possible_types, .. } => Some(possible_types),
708 MetaType::Union { possible_types, .. } => Some(possible_types),
709 _ => None,
710 }
711 }
712
713 pub fn type_overlap(&self, ty: &MetaType) -> bool {
714 if std::ptr::eq(self, ty) {
715 return true;
716 }
717
718 match (self.is_abstract(), ty.is_abstract()) {
719 (true, true) => self
720 .possible_types()
721 .iter()
722 .copied()
723 .flatten()
724 .any(|type_name| ty.is_possible_type(type_name)),
725 (true, false) => self.is_possible_type(ty.name()),
726 (false, true) => ty.is_possible_type(self.name()),
727 (false, false) => false,
728 }
729 }
730
731 pub fn rust_typename(&self) -> Option<&'static str> {
732 match self {
733 MetaType::Scalar { .. } => None,
734 MetaType::Object { rust_typename, .. } => *rust_typename,
735 MetaType::Interface { rust_typename, .. } => *rust_typename,
736 MetaType::Union { rust_typename, .. } => *rust_typename,
737 MetaType::Enum { rust_typename, .. } => *rust_typename,
738 MetaType::InputObject { rust_typename, .. } => *rust_typename,
739 }
740 }
741}
742
743pub struct MetaDirective {
744 pub name: String,
745 pub description: Option<String>,
746 pub locations: Vec<__DirectiveLocation>,
747 pub args: IndexMap<String, MetaInputValue>,
748 pub is_repeatable: bool,
749 pub visible: Option<MetaVisibleFn>,
750 pub composable: Option<String>,
751}
752
753impl MetaDirective {
754 pub(crate) fn sdl(&self, options: &SDLExportOptions) -> String {
755 let mut sdl = String::new();
756
757 if let Some(description) = &self.description {
758 self::export_sdl::write_description(&mut sdl, options, 0, description);
759 }
760
761 write!(sdl, "directive @{}", self.name).ok();
762
763 if !self.args.is_empty() {
764 let args = self
765 .args
766 .values()
767 .map(|value| self.argument_sdl(value))
768 .collect::<Vec<_>>()
769 .join(", ");
770 write!(sdl, "({})", args).ok();
771 }
772 let locations = self
773 .locations
774 .iter()
775 .map(|location| location.to_value().to_string())
776 .collect::<Vec<_>>()
777 .join(" | ");
778
779 if self.is_repeatable {
780 write!(sdl, " repeatable").ok();
781 }
782
783 write!(sdl, " on {}", locations).ok();
784 sdl
785 }
786
787 pub(crate) fn argument_sdl(&self, argument: &MetaInputValue) -> String {
788 let argument_default = match &argument.default_value {
789 Some(default) => format!(" = {default}"),
790 None => "".to_string(),
791 };
792
793 format!("{}: {}{}", argument.name, argument.ty, argument_default)
794 }
795}
796
797#[derive(Default)]
799pub struct Registry {
800 pub types: BTreeMap<String, MetaType>,
801 pub directives: BTreeMap<String, MetaDirective>,
802 pub implements: HashMap<String, IndexSet<String>>,
803 pub query_type: String,
804 pub mutation_type: Option<String>,
805 pub subscription_type: Option<String>,
806 pub introspection_mode: IntrospectionMode,
807 pub enable_federation: bool,
808 pub federation_subscription: bool,
809 pub ignore_name_conflicts: HashSet<String>,
810 pub enable_suggestions: bool,
811}
812
813impl Registry {
814 pub(crate) fn add_system_types(&mut self) {
815 self.add_directive(MetaDirective {
816 name: "skip".into(),
817 description: Some("Directs the executor to skip this field or fragment when the `if` argument is true.".to_string()),
818 locations: vec![
819 __DirectiveLocation::FIELD,
820 __DirectiveLocation::FRAGMENT_SPREAD,
821 __DirectiveLocation::INLINE_FRAGMENT
822 ],
823 args: {
824 let mut args = IndexMap::new();
825 args.insert("if".to_string(), MetaInputValue {
826 name: "if".to_string(),
827 description: Some("Skipped when true.".to_string()),
828 ty: "Boolean!".to_string(),
829 deprecation: Deprecation::NoDeprecated,
830 default_value: None,
831 visible: None,
832 inaccessible: false,
833 tags: Default::default(),
834 is_secret: false,
835 directive_invocations: vec![]
836 });
837 args
838 },
839 is_repeatable: false,
840 visible: None,
841 composable: None,
842 });
843
844 self.add_directive(MetaDirective {
845 name: "include".into(),
846 description: Some("Directs the executor to include this field or fragment only when the `if` argument is true.".to_string()),
847 locations: vec![
848 __DirectiveLocation::FIELD,
849 __DirectiveLocation::FRAGMENT_SPREAD,
850 __DirectiveLocation::INLINE_FRAGMENT
851 ],
852 args: {
853 let mut args = IndexMap::new();
854 args.insert("if".to_string(), MetaInputValue {
855 name: "if".to_string(),
856 description: Some("Included when true.".to_string()),
857 ty: "Boolean!".to_string(),
858 deprecation: Deprecation::NoDeprecated,
859 default_value: None,
860 visible: None,
861 inaccessible: false,
862 tags: Default::default(),
863 is_secret: false,
864 directive_invocations: vec![]
865 });
866 args
867 },
868 is_repeatable: false,
869 visible: None,
870 composable: None,
871 });
872
873 self.add_directive(MetaDirective {
874 name: "deprecated".into(),
875 description: Some(
876 "Marks an element of a GraphQL schema as no longer supported.".into(),
877 ),
878 locations: vec![
879 __DirectiveLocation::FIELD_DEFINITION,
880 __DirectiveLocation::ARGUMENT_DEFINITION,
881 __DirectiveLocation::INPUT_FIELD_DEFINITION,
882 __DirectiveLocation::ENUM_VALUE,
883 ],
884 args: {
885 let mut args = IndexMap::new();
886 args.insert(
887 "reason".into(),
888 MetaInputValue {
889 name: "reason".into(),
890 description: Some(
891 "A reason for why it is deprecated, formatted using Markdown syntax"
892 .into(),
893 ),
894 ty: "String".into(),
895 deprecation: Deprecation::NoDeprecated,
896 default_value: Some(r#""No longer supported""#.into()),
897 visible: None,
898 inaccessible: false,
899 tags: Default::default(),
900 is_secret: false,
901 directive_invocations: vec![],
902 },
903 );
904 args
905 },
906 is_repeatable: false,
907 visible: None,
908 composable: None,
909 });
910
911 self.add_directive(MetaDirective {
912 name: "specifiedBy".into(),
913 description: Some("Provides a scalar specification URL for specifying the behavior of custom scalar types.".into()),
914 locations: vec![__DirectiveLocation::SCALAR],
915 args: {
916 let mut args = IndexMap::new();
917 args.insert(
918 "url".into(),
919 MetaInputValue {
920 name: "url".into(),
921 description: Some("URL that specifies the behavior of this scalar.".into()),
922 ty: "String!".into(),
923 deprecation: Deprecation::NoDeprecated,
924 default_value: None,
925 visible: None,
926 inaccessible: false,
927 tags: Default::default(),
928 is_secret: false,
929 directive_invocations: vec![],
930 },
931 );
932 args
933 },
934 is_repeatable: false,
935 visible: None,
936 composable: None,
937 });
938
939 self.add_directive(MetaDirective {
940 name: "oneOf".into(),
941 description: Some(
942 "Indicates that an Input Object is a OneOf Input Object (and thus requires \
943 exactly one of its field be provided)"
944 .to_string(),
945 ),
946 locations: vec![__DirectiveLocation::INPUT_OBJECT],
947 args: Default::default(),
948 is_repeatable: false,
949 visible: None,
950 composable: None,
951 });
952
953 <bool as InputType>::create_type_info(self);
955 <i32 as InputType>::create_type_info(self);
956 <f32 as InputType>::create_type_info(self);
957 <String as InputType>::create_type_info(self);
958 <ID as InputType>::create_type_info(self);
959 }
960
961 pub fn create_input_type<T, F>(&mut self, type_id: MetaTypeId, mut f: F) -> String
962 where
963 T: InputType,
964 F: FnMut(&mut Registry) -> MetaType,
965 {
966 self.create_type(&mut f, &T::type_name(), std::any::type_name::<T>(), type_id);
967 T::qualified_type_name()
968 }
969
970 pub fn create_output_type<T, F>(&mut self, type_id: MetaTypeId, mut f: F) -> String
971 where
972 T: OutputType + ?Sized,
973 F: FnMut(&mut Registry) -> MetaType,
974 {
975 self.create_type(&mut f, &T::type_name(), std::any::type_name::<T>(), type_id);
976 T::qualified_type_name()
977 }
978
979 pub fn create_subscription_type<T, F>(&mut self, mut f: F) -> String
980 where
981 T: SubscriptionType + ?Sized,
982 F: FnMut(&mut Registry) -> MetaType,
983 {
984 self.create_type(
985 &mut f,
986 &T::type_name(),
987 std::any::type_name::<T>(),
988 MetaTypeId::Object,
989 );
990 T::qualified_type_name()
991 }
992
993 fn create_type<F: FnMut(&mut Registry) -> MetaType>(
994 &mut self,
995 f: &mut F,
996 name: &str,
997 rust_typename: &'static str,
998 type_id: MetaTypeId,
999 ) {
1000 match self.types.get(name) {
1001 Some(ty) => {
1002 if let Some(prev_typename) = ty.rust_typename() {
1003 if prev_typename == "__fake_type__" {
1004 return;
1005 }
1006
1007 if rust_typename != prev_typename && !self.ignore_name_conflicts.contains(name)
1008 {
1009 panic!(
1010 "`{}` and `{}` have the same GraphQL name `{}`",
1011 prev_typename, rust_typename, name,
1012 );
1013 }
1014
1015 if ty.type_id() != type_id {
1016 panic!(
1017 "Register `{}` as `{}`, but it is already registered as `{}`",
1018 name,
1019 type_id,
1020 ty.type_id()
1021 );
1022 }
1023 }
1024 }
1025 None => {
1026 self.types
1029 .insert(name.to_string(), type_id.create_fake_type(rust_typename));
1030 let ty = f(self);
1031 *self.types.get_mut(name).unwrap() = ty;
1032 }
1033 }
1034 }
1035
1036 pub fn create_fake_output_type<T: OutputType>(&mut self) -> MetaType {
1037 T::create_type_info(self);
1038 self.types
1039 .get(&*T::type_name())
1040 .cloned()
1041 .expect("You definitely encountered a bug!")
1042 }
1043
1044 pub fn create_fake_input_type<T: InputType>(&mut self) -> MetaType {
1045 T::create_type_info(self);
1046 self.types
1047 .get(&*T::type_name())
1048 .cloned()
1049 .expect("You definitely encountered a bug!")
1050 }
1051
1052 pub fn create_fake_subscription_type<T: SubscriptionType>(&mut self) -> MetaType {
1053 T::create_type_info(self);
1054 self.types
1055 .get(&*T::type_name())
1056 .cloned()
1057 .expect("You definitely encountered a bug!")
1058 }
1059
1060 pub fn add_directive(&mut self, directive: MetaDirective) {
1061 self.directives
1062 .insert(directive.name.to_string(), directive);
1063 }
1064
1065 pub fn add_implements(&mut self, ty: &str, interface: &str) {
1066 self.implements
1067 .entry(ty.to_string())
1068 .and_modify(|interfaces| {
1069 interfaces.insert(interface.to_string());
1070 })
1071 .or_insert({
1072 let mut interfaces = IndexSet::new();
1073 interfaces.insert(interface.to_string());
1074 interfaces
1075 });
1076 }
1077
1078 pub fn add_keys(&mut self, ty: &str, keys: impl Into<String>) {
1079 let all_keys = match self.types.get_mut(ty) {
1080 Some(MetaType::Object { keys: all_keys, .. }) => all_keys,
1081 Some(MetaType::Interface { keys: all_keys, .. }) => all_keys,
1082 _ => return,
1083 };
1084 if let Some(all_keys) = all_keys {
1085 all_keys.push(keys.into());
1086 } else {
1087 *all_keys = Some(vec![keys.into()]);
1088 }
1089 }
1090
1091 pub fn concrete_type_by_name(&self, type_name: &str) -> Option<&MetaType> {
1092 self.types.get(MetaTypeName::concrete_typename(type_name))
1093 }
1094
1095 pub fn concrete_type_by_parsed_type(&self, query_type: &ParsedType) -> Option<&MetaType> {
1096 match &query_type.base {
1097 ParsedBaseType::Named(name) => self.types.get(name.as_str()),
1098 ParsedBaseType::List(ty) => self.concrete_type_by_parsed_type(ty),
1099 }
1100 }
1101
1102 pub(crate) fn has_entities(&self) -> bool {
1103 self.types.values().any(|ty| match ty {
1104 MetaType::Object {
1105 keys: Some(keys),
1106 resolvable: true,
1107 ..
1108 }
1109 | MetaType::Interface {
1110 keys: Some(keys), ..
1111 } => !keys.is_empty(),
1112 _ => false,
1113 })
1114 }
1115
1116 fn create_entity_type_and_root_field(&mut self) {
1122 let possible_types: IndexSet<String> = self
1123 .types
1124 .values()
1125 .filter_map(|ty| match ty {
1126 MetaType::Object {
1127 name,
1128 keys: Some(keys),
1129 resolvable: true,
1130 ..
1131 } if !keys.is_empty() => Some(name.clone()),
1132 MetaType::Interface {
1133 name,
1134 keys: Some(keys),
1135 ..
1136 } if !keys.is_empty() => Some(name.clone()),
1137 _ => None,
1138 })
1139 .collect();
1140
1141 if let MetaType::Object { fields, .. } = self
1142 .types
1143 .get_mut(&self.query_type)
1144 .expect("missing query type")
1145 {
1146 fields.insert(
1147 "_service".to_string(),
1148 MetaField {
1149 name: "_service".to_string(),
1150 description: None,
1151 args: Default::default(),
1152 ty: "_Service!".to_string(),
1153 deprecation: Default::default(),
1154 cache_control: Default::default(),
1155 external: false,
1156 requires: None,
1157 provides: None,
1158 shareable: false,
1159 inaccessible: false,
1160 tags: Default::default(),
1161 override_from: None,
1162 visible: None,
1163 compute_complexity: None,
1164 directive_invocations: vec![],
1165 requires_scopes: vec![],
1166 },
1167 );
1168 }
1169
1170 if !possible_types.is_empty() {
1171 self.types.insert(
1172 "_Entity".to_string(),
1173 MetaType::Union {
1174 name: "_Entity".to_string(),
1175 description: None,
1176 possible_types,
1177 visible: None,
1178 inaccessible: false,
1179 tags: Default::default(),
1180 rust_typename: Some("async_graphql::federation::Entity"),
1181 directive_invocations: vec![],
1182 },
1183 );
1184
1185 if let MetaType::Object { fields, .. } = self.types.get_mut(&self.query_type).unwrap() {
1186 fields.insert(
1187 "_entities".to_string(),
1188 MetaField {
1189 name: "_entities".to_string(),
1190 description: None,
1191 args: {
1192 let mut args = IndexMap::new();
1193 args.insert(
1194 "representations".to_string(),
1195 MetaInputValue {
1196 name: "representations".to_string(),
1197 description: None,
1198 ty: "[_Any!]!".to_string(),
1199 deprecation: Deprecation::NoDeprecated,
1200 default_value: None,
1201 visible: None,
1202 inaccessible: false,
1203 tags: Default::default(),
1204 is_secret: false,
1205 directive_invocations: vec![],
1206 },
1207 );
1208 args
1209 },
1210 ty: "[_Entity]!".to_string(),
1211 deprecation: Default::default(),
1212 cache_control: Default::default(),
1213 external: false,
1214 requires: None,
1215 provides: None,
1216 shareable: false,
1217 visible: None,
1218 inaccessible: false,
1219 tags: Default::default(),
1220 override_from: None,
1221 compute_complexity: None,
1222 directive_invocations: vec![],
1223 requires_scopes: vec![],
1224 },
1225 );
1226 }
1227 }
1228 }
1229
1230 pub(crate) fn create_introspection_types(&mut self) {
1231 __Schema::create_type_info(self);
1232
1233 if let Some(MetaType::Object { fields, .. }) = self.types.get_mut(&self.query_type) {
1234 fields.insert(
1235 "__schema".to_string(),
1236 MetaField {
1237 name: "__schema".to_string(),
1238 description: Some("Access the current type schema of this server.".to_string()),
1239 args: Default::default(),
1240 ty: "__Schema".to_string(),
1241 deprecation: Default::default(),
1242 cache_control: Default::default(),
1243 external: false,
1244 requires: None,
1245 provides: None,
1246 shareable: false,
1247 inaccessible: false,
1248 tags: Default::default(),
1249 visible: None,
1250 compute_complexity: None,
1251 override_from: None,
1252 directive_invocations: vec![],
1253 requires_scopes: vec![],
1254 },
1255 );
1256
1257 fields.insert(
1258 "__type".to_string(),
1259 MetaField {
1260 name: "__type".to_string(),
1261 description: Some("Request the type information of a single type.".to_string()),
1262 args: {
1263 let mut args = IndexMap::new();
1264 args.insert(
1265 "name".to_string(),
1266 MetaInputValue {
1267 name: "name".to_string(),
1268 description: None,
1269 ty: "String!".to_string(),
1270 deprecation: Deprecation::NoDeprecated,
1271 default_value: None,
1272 visible: None,
1273 inaccessible: false,
1274 tags: Default::default(),
1275 is_secret: false,
1276 directive_invocations: vec![],
1277 },
1278 );
1279 args
1280 },
1281 ty: "__Type".to_string(),
1282 deprecation: Default::default(),
1283 cache_control: Default::default(),
1284 external: false,
1285 requires: None,
1286 provides: None,
1287 shareable: false,
1288 inaccessible: false,
1289 tags: Default::default(),
1290 override_from: None,
1291 visible: None,
1292 compute_complexity: None,
1293 directive_invocations: vec![],
1294 requires_scopes: vec![],
1295 },
1296 );
1297 }
1298 }
1299
1300 pub(crate) fn create_federation_types(&mut self) {
1301 <Any as InputType>::create_type_info(self);
1302
1303 self.types.insert(
1304 "_Service".to_string(),
1305 MetaType::Object {
1306 name: "_Service".to_string(),
1307 description: None,
1308 fields: {
1309 let mut fields = IndexMap::new();
1310 fields.insert(
1311 "sdl".to_string(),
1312 MetaField {
1313 name: "sdl".to_string(),
1314 description: None,
1315 args: Default::default(),
1316 ty: "String".to_string(),
1317 deprecation: Default::default(),
1318 cache_control: Default::default(),
1319 external: false,
1320 requires: None,
1321 provides: None,
1322 shareable: false,
1323 visible: None,
1324 inaccessible: false,
1325 tags: Default::default(),
1326 override_from: None,
1327 compute_complexity: None,
1328 directive_invocations: vec![],
1329 requires_scopes: vec![],
1330 },
1331 );
1332 fields
1333 },
1334 cache_control: Default::default(),
1335 extends: false,
1336 shareable: false,
1337 resolvable: true,
1338 interface_object: false,
1339 keys: None,
1340 visible: None,
1341 inaccessible: false,
1342 tags: Default::default(),
1343 is_subscription: false,
1344 rust_typename: Some("async_graphql::federation::Service"),
1345 directive_invocations: vec![],
1346 requires_scopes: vec![],
1347 },
1348 );
1349
1350 self.create_entity_type_and_root_field();
1351 }
1352
1353 pub fn names(&self) -> Vec<String> {
1354 let mut names = HashSet::new();
1355
1356 for d in self.directives.values() {
1357 names.insert(d.name.to_string());
1358 names.extend(d.args.values().map(|arg| arg.name.to_string()));
1359 }
1360
1361 for ty in self.types.values() {
1362 match ty {
1363 MetaType::Scalar { name, .. } | MetaType::Union { name, .. } => {
1364 names.insert(name.clone());
1365 }
1366 MetaType::Object { name, fields, .. }
1367 | MetaType::Interface { name, fields, .. } => {
1368 names.insert(name.clone());
1369 names.extend(
1370 fields
1371 .values()
1372 .map(|field| {
1373 std::iter::once(field.name.clone())
1374 .chain(field.args.values().map(|arg| arg.name.to_string()))
1375 })
1376 .flatten(),
1377 );
1378 }
1379 MetaType::Enum {
1380 name, enum_values, ..
1381 } => {
1382 names.insert(name.clone());
1383 names.extend(enum_values.values().map(|value| value.name.to_string()));
1384 }
1385 MetaType::InputObject {
1386 name, input_fields, ..
1387 } => {
1388 names.insert(name.clone());
1389 names.extend(input_fields.values().map(|field| field.name.to_string()));
1390 }
1391 }
1392 }
1393
1394 names.into_iter().collect()
1395 }
1396
1397 pub fn set_description(&mut self, name: impl AsRef<str>, desc: impl Into<String>) {
1398 let desc = desc.into();
1399 match self.types.get_mut(name.as_ref()) {
1400 Some(MetaType::Scalar { description, .. }) => *description = Some(desc),
1401 Some(MetaType::Object { description, .. }) => *description = Some(desc),
1402 Some(MetaType::Interface { description, .. }) => *description = Some(desc),
1403 Some(MetaType::Union { description, .. }) => *description = Some(desc),
1404 Some(MetaType::Enum { description, .. }) => *description = Some(desc),
1405 Some(MetaType::InputObject { description, .. }) => *description = Some(desc),
1406 None => {}
1407 }
1408 }
1409
1410 pub fn remove_unused_types(&mut self) {
1411 let mut used_types = BTreeSet::new();
1412 let mut unused_types = BTreeSet::new();
1413
1414 fn traverse_field<'a>(
1415 types: &'a BTreeMap<String, MetaType>,
1416 used_types: &mut BTreeSet<&'a str>,
1417 field: &'a MetaField,
1418 ) {
1419 traverse_type(
1420 types,
1421 used_types,
1422 MetaTypeName::concrete_typename(&field.ty),
1423 );
1424 for arg in field.args.values() {
1425 traverse_input_value(types, used_types, arg);
1426 }
1427 }
1428
1429 fn traverse_input_value<'a>(
1430 types: &'a BTreeMap<String, MetaType>,
1431 used_types: &mut BTreeSet<&'a str>,
1432 input_value: &'a MetaInputValue,
1433 ) {
1434 traverse_type(
1435 types,
1436 used_types,
1437 MetaTypeName::concrete_typename(&input_value.ty),
1438 );
1439 }
1440
1441 fn traverse_type<'a>(
1442 types: &'a BTreeMap<String, MetaType>,
1443 used_types: &mut BTreeSet<&'a str>,
1444 type_name: &'a str,
1445 ) {
1446 if used_types.contains(type_name) {
1447 return;
1448 }
1449
1450 if let Some(ty) = types.get(type_name) {
1451 used_types.insert(type_name);
1452 match ty {
1453 MetaType::Object { fields, .. } => {
1454 for field in fields.values() {
1455 traverse_field(types, used_types, field);
1456 }
1457 }
1458 MetaType::Interface {
1459 fields,
1460 possible_types,
1461 ..
1462 } => {
1463 for field in fields.values() {
1464 traverse_field(types, used_types, field);
1465 }
1466 for type_name in possible_types.iter() {
1467 traverse_type(types, used_types, type_name);
1468 }
1469 }
1470 MetaType::Union { possible_types, .. } => {
1471 for type_name in possible_types.iter() {
1472 traverse_type(types, used_types, type_name);
1473 }
1474 }
1475 MetaType::InputObject { input_fields, .. } => {
1476 for field in input_fields.values() {
1477 traverse_input_value(types, used_types, field);
1478 }
1479 }
1480 _ => {}
1481 }
1482 }
1483 }
1484
1485 for directive in self.directives.values() {
1486 for arg in directive.args.values() {
1487 traverse_input_value(&self.types, &mut used_types, arg);
1488 }
1489 }
1490
1491 for type_name in Some(&self.query_type)
1492 .into_iter()
1493 .chain(self.mutation_type.iter())
1494 .chain(self.subscription_type.iter())
1495 {
1496 traverse_type(&self.types, &mut used_types, type_name);
1497 }
1498
1499 for ty in self.types.values().filter(|ty| match ty {
1500 MetaType::Object {
1501 keys: Some(keys), ..
1502 }
1503 | MetaType::Interface {
1504 keys: Some(keys), ..
1505 } => !keys.is_empty(),
1506 _ => false,
1507 }) {
1508 traverse_type(&self.types, &mut used_types, ty.name());
1509 }
1510
1511 for ty in self.types.values() {
1512 let name = ty.name();
1513 if !is_system_type(name) && !used_types.contains(name) {
1514 unused_types.insert(name.to_string());
1515 }
1516 }
1517
1518 for type_name in unused_types {
1519 self.types.remove(&type_name);
1520 }
1521 }
1522
1523 pub fn find_visible_types(&self, ctx: &Context<'_>) -> HashSet<&str> {
1524 let mut visible_types = HashSet::new();
1525
1526 fn traverse_field<'a>(
1527 ctx: &Context<'_>,
1528 types: &'a BTreeMap<String, MetaType>,
1529 visible_types: &mut HashSet<&'a str>,
1530 field: &'a MetaField,
1531 ) {
1532 if !is_visible(ctx, &field.visible) {
1533 return;
1534 }
1535
1536 traverse_type(
1537 ctx,
1538 types,
1539 visible_types,
1540 MetaTypeName::concrete_typename(&field.ty),
1541 );
1542 for arg in field.args.values() {
1543 traverse_input_value(ctx, types, visible_types, arg);
1544 }
1545 }
1546
1547 fn traverse_input_value<'a>(
1548 ctx: &Context<'_>,
1549 types: &'a BTreeMap<String, MetaType>,
1550 visible_types: &mut HashSet<&'a str>,
1551 input_value: &'a MetaInputValue,
1552 ) {
1553 if !is_visible(ctx, &input_value.visible) {
1554 return;
1555 }
1556
1557 traverse_type(
1558 ctx,
1559 types,
1560 visible_types,
1561 MetaTypeName::concrete_typename(&input_value.ty),
1562 );
1563 }
1564
1565 fn traverse_type<'a>(
1566 ctx: &Context<'_>,
1567 types: &'a BTreeMap<String, MetaType>,
1568 visible_types: &mut HashSet<&'a str>,
1569 type_name: &'a str,
1570 ) {
1571 if visible_types.contains(type_name) {
1572 return;
1573 }
1574
1575 if let Some(ty) = types.get(type_name) {
1576 if !ty.is_visible(ctx) {
1577 return;
1578 }
1579
1580 visible_types.insert(type_name);
1581 match ty {
1582 MetaType::Object { fields, .. } => {
1583 for field in fields.values() {
1584 traverse_field(ctx, types, visible_types, field);
1585 }
1586 }
1587 MetaType::Interface {
1588 fields,
1589 possible_types,
1590 ..
1591 } => {
1592 for field in fields.values() {
1593 traverse_field(ctx, types, visible_types, field);
1594 }
1595 for type_name in possible_types.iter() {
1596 traverse_type(ctx, types, visible_types, type_name);
1597 }
1598 }
1599 MetaType::Union { possible_types, .. } => {
1600 for type_name in possible_types.iter() {
1601 traverse_type(ctx, types, visible_types, type_name);
1602 }
1603 }
1604 MetaType::InputObject { input_fields, .. } => {
1605 for field in input_fields.values() {
1606 traverse_input_value(ctx, types, visible_types, field);
1607 }
1608 }
1609 _ => {}
1610 }
1611 }
1612 }
1613
1614 for directive in self.directives.values() {
1615 if is_visible(ctx, &directive.visible) {
1616 for arg in directive.args.values() {
1617 traverse_input_value(ctx, &self.types, &mut visible_types, arg);
1618 }
1619 }
1620 }
1621
1622 for type_name in Some(&self.query_type)
1623 .into_iter()
1624 .chain(self.mutation_type.iter())
1625 .chain(self.subscription_type.iter())
1626 {
1627 traverse_type(ctx, &self.types, &mut visible_types, type_name);
1628 }
1629
1630 for ty in self.types.values().filter(|ty| match ty {
1631 MetaType::Object {
1632 keys: Some(keys), ..
1633 }
1634 | MetaType::Interface {
1635 keys: Some(keys), ..
1636 } => !keys.is_empty(),
1637 _ => false,
1638 }) {
1639 traverse_type(ctx, &self.types, &mut visible_types, ty.name());
1640 }
1641
1642 for ty in self.types.values() {
1643 if let MetaType::Interface { possible_types, .. } = ty
1644 && ty.is_visible(ctx)
1645 && !visible_types.contains(ty.name())
1646 {
1647 for type_name in possible_types.iter() {
1648 if visible_types.contains(type_name.as_str()) {
1649 traverse_type(ctx, &self.types, &mut visible_types, ty.name());
1650 break;
1651 }
1652 }
1653 }
1654 }
1655
1656 self.types
1657 .values()
1658 .filter_map(|ty| {
1659 let name = ty.name();
1660 if is_system_type(name) || visible_types.contains(name) {
1661 Some(name)
1662 } else {
1663 None
1664 }
1665 })
1666 .collect()
1667 }
1668}
1669
1670pub(crate) fn is_visible(ctx: &Context<'_>, visible: &Option<MetaVisibleFn>) -> bool {
1671 match visible {
1672 Some(f) => f(ctx),
1673 None => true,
1674 }
1675}
1676
1677fn is_system_type(name: &str) -> bool {
1678 if name.starts_with("__") {
1679 return true;
1680 }
1681
1682 name == "Boolean" || name == "Int" || name == "Float" || name == "String" || name == "ID"
1683}
1684
1685#[cfg(test)]
1686mod test {
1687 use crate::{
1688 SDLExportOptions,
1689 registry::{__DirectiveLocation, MetaDirective, MetaDirectiveInvocation},
1690 };
1691
1692 #[test]
1693 fn test_directive_invocation_dsl() {
1694 let expected = r#"@testDirective(int_value: 1, str_value: "abc")"#;
1695 assert_eq!(
1696 expected.to_string(),
1697 MetaDirectiveInvocation {
1698 name: "testDirective".to_string(),
1699 args: [
1700 ("int_value".to_string(), 1u32.into()),
1701 ("str_value".to_string(), "abc".into())
1702 ]
1703 .into(),
1704 }
1705 .sdl()
1706 )
1707 }
1708
1709 #[test]
1710 fn test_repeatable_directive_dsl() {
1711 let expected = r#"directive @testDirective repeatable on OBJECT | INTERFACE"#;
1712 let export_options = SDLExportOptions::default();
1713
1714 assert_eq!(
1715 expected.to_string(),
1716 MetaDirective {
1717 name: "testDirective".to_string(),
1718 description: None,
1719 locations: vec![__DirectiveLocation::OBJECT, __DirectiveLocation::INTERFACE,],
1720 args: Default::default(),
1721 is_repeatable: true,
1722 visible: None,
1723 composable: None,
1724 }
1725 .sdl(&export_options)
1726 )
1727 }
1728
1729 #[test]
1730 fn test_non_repeatable_directive_dsl() {
1731 let expected = r#"directive @testDirective on OBJECT | INTERFACE"#;
1732 let export_options = SDLExportOptions::default();
1733
1734 assert_eq!(
1735 expected.to_string(),
1736 MetaDirective {
1737 name: "testDirective".to_string(),
1738 description: None,
1739 locations: vec![__DirectiveLocation::OBJECT, __DirectiveLocation::INTERFACE,],
1740 args: Default::default(),
1741 is_repeatable: false,
1742 visible: None,
1743 composable: None,
1744 }
1745 .sdl(&export_options)
1746 )
1747 }
1748}