1use std::collections::HashMap;
2use std::ops::Deref;
3
4use indexmap::{IndexMap, IndexSet};
5use parser::types::{
6 self, BaseType, ConstDirective, DirectiveDefinition, DirectiveLocation, DocumentOperations,
7 EnumType, InputObjectType, InterfaceType, ObjectType, SchemaDefinition, Selection,
8 SelectionSet, ServiceDocument, Type, TypeDefinition, TypeSystemDefinition, UnionType,
9};
10use parser::{Positioned, Result};
11use value::{ConstValue, Name};
12
13use crate::type_ext::TypeExt;
14use crate::CombineError;
15
16#[derive(Debug, Eq, PartialEq)]
17pub enum Deprecation {
18 NoDeprecated,
19 Deprecated { reason: Option<String> },
20}
21
22impl Deprecation {
23 #[inline]
24 pub fn is_deprecated(&self) -> bool {
25 matches!(self, Deprecation::Deprecated { .. })
26 }
27
28 #[inline]
29 pub fn reason(&self) -> Option<&str> {
30 match self {
31 Deprecation::NoDeprecated => None,
32 Deprecation::Deprecated { reason } => reason.as_deref(),
33 }
34 }
35}
36
37#[derive(Debug, Eq, PartialEq)]
38pub struct MetaField {
39 pub description: Option<String>,
40 pub name: Name,
41 pub arguments: IndexMap<Name, MetaInputValue>,
42 pub ty: Type,
43 pub deprecation: Deprecation,
44
45 pub service: Option<String>,
46 pub requires: Option<KeyFields>,
47 pub provides: Option<KeyFields>,
48}
49
50#[derive(Debug, Eq, PartialEq, Copy, Clone)]
51pub enum TypeKind {
52 Scalar,
53 Object,
54 Interface,
55 Union,
56 Enum,
57 InputObject,
58}
59
60#[derive(Debug, Eq, PartialEq)]
61pub struct KeyFields(IndexMap<Name, KeyFields>);
62
63impl Deref for KeyFields {
64 type Target = IndexMap<Name, KeyFields>;
65
66 fn deref(&self) -> &Self::Target {
67 &self.0
68 }
69}
70
71#[derive(Debug, Eq, PartialEq)]
72pub struct MetaEnumValue {
73 pub description: Option<String>,
74 pub value: Name,
75 pub deprecation: Deprecation,
76}
77
78#[derive(Debug, Eq, PartialEq)]
79pub struct MetaInputValue {
80 pub description: Option<String>,
81 pub name: Name,
82 pub ty: Type,
83 pub default_value: Option<ConstValue>,
84}
85
86#[derive(Debug, Eq, PartialEq)]
87pub struct MetaType {
88 pub description: Option<String>,
89 pub name: Name,
90 pub kind: TypeKind,
91 pub owner: Option<String>,
92 pub keys: HashMap<String, Vec<KeyFields>>,
93
94 pub implements: IndexSet<Name>,
95 pub fields: IndexMap<Name, MetaField>,
96 pub possible_types: IndexSet<Name>,
97 pub enum_values: IndexMap<Name, MetaEnumValue>,
98 pub input_fields: IndexMap<Name, MetaInputValue>,
99}
100
101impl MetaType {
102 #[inline]
103 pub fn field_by_name(&self, name: &str) -> Option<&MetaField> {
104 self.fields.get(name)
105 }
106
107 #[inline]
108 pub fn is_composite(&self) -> bool {
109 matches!(
110 self.kind,
111 TypeKind::Object | TypeKind::Interface | TypeKind::Union
112 )
113 }
114
115 #[inline]
116 pub fn is_abstract(&self) -> bool {
117 matches!(self.kind, TypeKind::Interface | TypeKind::Union)
118 }
119
120 #[inline]
121 pub fn is_leaf(&self) -> bool {
122 matches!(self.kind, TypeKind::Enum | TypeKind::Scalar)
123 }
124
125 #[inline]
126 pub fn is_input(&self) -> bool {
127 matches!(
128 self.kind,
129 TypeKind::Enum | TypeKind::Scalar | TypeKind::InputObject
130 )
131 }
132
133 #[inline]
134 pub fn is_possible_type(&self, type_name: &str) -> bool {
135 match self.kind {
136 TypeKind::Interface | TypeKind::Union => self.possible_types.contains(type_name),
137 TypeKind::Object => self.name == type_name,
138 _ => false,
139 }
140 }
141
142 pub fn type_overlap(&self, ty: &MetaType) -> bool {
143 if std::ptr::eq(self, ty) {
144 return true;
145 }
146
147 match (self.is_abstract(), ty.is_abstract()) {
148 (true, true) => self
149 .possible_types
150 .iter()
151 .any(|type_name| ty.is_possible_type(type_name)),
152 (true, false) => self.is_possible_type(&ty.name),
153 (false, true) => ty.is_possible_type(&self.name),
154 (false, false) => false,
155 }
156 }
157}
158
159#[derive(Debug)]
160pub struct MetaDirective {
161 pub name: Name,
162 pub description: Option<String>,
163 pub locations: Vec<DirectiveLocation>,
164 pub arguments: IndexMap<Name, MetaInputValue>,
165}
166
167#[derive(Debug, Default)]
168pub struct ComposedSchema {
169 pub query_type: Option<Name>,
170 pub mutation_type: Option<Name>,
171 pub subscription_type: Option<Name>,
172 pub types: IndexMap<Name, MetaType>,
173 pub directives: HashMap<Name, MetaDirective>,
174}
175
176impl ComposedSchema {
177 pub fn parse(document: &str) -> Result<ComposedSchema> {
178 Ok(Self::new(parser::parse_schema(document)?))
179 }
180
181 pub fn new(document: ServiceDocument) -> ComposedSchema {
182 let mut composed_schema = ComposedSchema::default();
183
184 for definition in document.definitions.into_iter() {
185 match definition {
186 TypeSystemDefinition::Schema(schema) => {
187 convert_schema_definition(&mut composed_schema, schema.node);
188 }
189 TypeSystemDefinition::Type(type_definition) => {
190 composed_schema.types.insert(
191 type_definition.node.name.node.clone(),
192 convert_type_definition(type_definition.node),
193 );
194 }
195 TypeSystemDefinition::Directive(_) => {}
196 }
197 }
198
199 finish_schema(&mut composed_schema);
200 composed_schema
201 }
202
203 pub fn combine(
204 federation_sdl: impl IntoIterator<Item = (String, ServiceDocument)>,
205 ) -> ::std::result::Result<Self, CombineError> {
206 let mut composed_schema = ComposedSchema::default();
207 let root_objects = &["Query", "Mutation", "Subscription"];
208
209 for obj in root_objects {
210 let name = Name::new(obj);
211 composed_schema.types.insert(
212 name.clone(),
213 MetaType {
214 description: None,
215 name,
216 kind: TypeKind::Object,
217 owner: None,
218 keys: Default::default(),
219 implements: Default::default(),
220 fields: Default::default(),
221 possible_types: Default::default(),
222 enum_values: Default::default(),
223 input_fields: Default::default(),
224 },
225 );
226 }
227
228 composed_schema.query_type = Some(Name::new("Query"));
229 composed_schema.mutation_type = Some(Name::new("Mutation"));
230 composed_schema.subscription_type = Some(Name::new("Subscription"));
231
232 for (service, doc) in federation_sdl {
233 for definition in doc.definitions {
234 match definition {
235 TypeSystemDefinition::Type(type_definition) => {
236 if let types::TypeKind::Object(ObjectType { implements, fields }) =
237 type_definition.node.kind
238 {
239 let name = type_definition.node.name.node.clone();
240 let description = type_definition
241 .node
242 .description
243 .map(|description| description.node);
244 let is_extend =
245 type_definition.node.extend || root_objects.contains(&&*name);
246 let meta_type = composed_schema
247 .types
248 .entry(name.clone())
249 .or_insert_with(|| MetaType {
250 description,
251 name,
252 kind: TypeKind::Object,
253 owner: None,
254 keys: Default::default(),
255 implements: Default::default(),
256 fields: Default::default(),
257 possible_types: Default::default(),
258 enum_values: Default::default(),
259 input_fields: Default::default(),
260 });
261
262 if !is_extend {
263 meta_type.owner = Some(service.clone());
264 };
265
266 for directive in type_definition.node.directives {
267 if directive.node.name.node.as_str() == "key" {
268 if let Some(fields) =
269 get_argument_str(&directive.node.arguments, "fields")
270 {
271 if let Some(selection_set) =
272 parse_fields(fields.node).map(|selection_set| {
273 Positioned::new(selection_set, directive.pos)
274 })
275 {
276 meta_type
277 .keys
278 .entry(service.clone())
279 .or_default()
280 .push(convert_key_fields(selection_set.node));
281 }
282 }
283 }
284 }
285
286 meta_type
287 .implements
288 .extend(implements.into_iter().map(|implement| implement.node));
289
290 for field in fields {
291 if is_extend {
292 let is_external =
293 has_directive(&field.node.directives, "external");
294 if is_external {
295 continue;
296 }
297 }
298
299 if meta_type.fields.contains_key(&field.node.name.node) {
300 return Err(CombineError::FieldConflicted {
301 type_name: type_definition.node.name.node.to_string(),
302 field_name: field.node.name.node.to_string(),
303 });
304 }
305 let mut meta_field = convert_field_definition(field.node);
306 if is_extend {
307 meta_field.service = Some(service.clone());
308 }
309 meta_type.fields.insert(meta_field.name.clone(), meta_field);
310 }
311 } else {
312 let meta_type = convert_type_definition(type_definition.node);
313 if let Some(meta_type2) = composed_schema.types.get(&meta_type.name) {
314 if meta_type2 != &meta_type {
315 return Err(CombineError::DefinitionConflicted {
316 type_name: meta_type.name.to_string(),
317 });
318 }
319 }
320 composed_schema
321 .types
322 .insert(meta_type.name.clone(), meta_type);
323 }
324 }
325 TypeSystemDefinition::Schema(_schema_definition) => {
326 return Err(CombineError::SchemaIsNotAllowed)
327 }
328 TypeSystemDefinition::Directive(_directive_definition) => {}
329 }
330 }
331 }
332
333 if let Some(mutation) = composed_schema.types.get("Mutation") {
334 if mutation.fields.is_empty() {
335 composed_schema.types.remove("Mutation");
336 composed_schema.mutation_type = None;
337 }
338 }
339
340 if let Some(subscription) = composed_schema.types.get("Subscription") {
341 if subscription.fields.is_empty() {
342 composed_schema.types.remove("Subscription");
343 composed_schema.subscription_type = None;
344 }
345 }
346
347 finish_schema(&mut composed_schema);
348 Ok(composed_schema)
349 }
350
351 #[inline]
352 pub fn query_type(&self) -> &str {
353 self.query_type
354 .as_ref()
355 .map(|name| name.as_str())
356 .unwrap_or("Query")
357 }
358
359 #[inline]
360 pub fn mutation_type(&self) -> Option<&str> {
361 self.mutation_type.as_ref().map(|name| name.as_str())
362 }
363
364 #[inline]
365 pub fn subscription_type(&self) -> Option<&str> {
366 self.subscription_type.as_ref().map(|name| name.as_str())
367 }
368
369 #[inline]
370 pub fn get_type(&self, ty: &Type) -> Option<&MetaType> {
371 let name = match &ty.base {
372 BaseType::Named(name) => name.as_str(),
373 BaseType::List(ty) => return self.get_type(ty),
374 };
375 self.types.get(name)
376 }
377
378 pub fn concrete_type_by_name(&self, ty: &Type) -> Option<&MetaType> {
379 self.types.get(ty.concrete_typename())
380 }
381}
382
383fn get_argument<'a>(
384 arguments: &'a [(Positioned<Name>, Positioned<ConstValue>)],
385 name: &str,
386) -> Option<&'a Positioned<ConstValue>> {
387 arguments.iter().find_map(|d| {
388 if d.0.node.as_str() == name {
389 Some(&d.1)
390 } else {
391 None
392 }
393 })
394}
395
396fn get_argument_str<'a>(
397 arguments: &'a [(Positioned<Name>, Positioned<ConstValue>)],
398 name: &str,
399) -> Option<Positioned<&'a str>> {
400 get_argument(arguments, name).and_then(|value| match &value.node {
401 ConstValue::String(s) => Some(Positioned::new(s.as_str(), value.pos)),
402 _ => None,
403 })
404}
405
406fn parse_fields(fields: &str) -> Option<SelectionSet> {
407 parser::parse_query(format!("{{{}}}", fields))
408 .ok()
409 .and_then(|document| match document.operations {
410 DocumentOperations::Single(op) => Some(op.node.selection_set.node),
411 DocumentOperations::Multiple(_) => None,
412 })
413}
414
415fn convert_schema_definition(
416 composed_schema: &mut ComposedSchema,
417 schema_definition: SchemaDefinition,
418) {
419 composed_schema.query_type = schema_definition.query.map(|name| name.node);
420 composed_schema.mutation_type = schema_definition.mutation.map(|name| name.node);
421 composed_schema.subscription_type = schema_definition.subscription.map(|name| name.node);
422}
423
424fn convert_type_definition(definition: TypeDefinition) -> MetaType {
425 let mut type_definition = MetaType {
426 description: definition.description.map(|description| description.node),
427 name: definition.name.node.clone(),
428 kind: TypeKind::Scalar,
429 owner: None,
430 keys: Default::default(),
431 implements: Default::default(),
432 fields: Default::default(),
433 possible_types: Default::default(),
434 enum_values: Default::default(),
435 input_fields: Default::default(),
436 };
437
438 match definition.kind {
439 types::TypeKind::Scalar => type_definition.kind = TypeKind::Scalar,
440 types::TypeKind::Object(ObjectType { implements, fields }) => {
441 type_definition.kind = TypeKind::Object;
442 type_definition.implements = implements
443 .into_iter()
444 .map(|implement| implement.node)
445 .collect();
446 type_definition
447 .fields
448 .extend(fields.into_iter().map(|field| {
449 (
450 field.node.name.node.clone(),
451 convert_field_definition(field.node),
452 )
453 }));
454 }
455 types::TypeKind::Interface(InterfaceType { implements, fields }) => {
456 type_definition.kind = TypeKind::Interface;
457 type_definition.implements = implements.into_iter().map(|name| name.node).collect();
458 type_definition.fields = fields
459 .into_iter()
460 .map(|field| {
461 (
462 field.node.name.node.clone(),
463 convert_field_definition(field.node),
464 )
465 })
466 .collect();
467 }
468 types::TypeKind::Union(UnionType { members }) => {
469 type_definition.kind = TypeKind::Union;
470 type_definition.possible_types = members.into_iter().map(|name| name.node).collect();
471 }
472 types::TypeKind::Enum(EnumType { values }) => {
473 type_definition.kind = TypeKind::Enum;
474 type_definition
475 .enum_values
476 .extend(values.into_iter().map(|value| {
477 (
478 value.node.value.node.clone(),
479 MetaEnumValue {
480 description: value.node.description.map(|description| description.node),
481 value: value.node.value.node,
482 deprecation: get_deprecated(&value.node.directives),
483 },
484 )
485 }));
486 }
487 types::TypeKind::InputObject(InputObjectType { fields }) => {
488 type_definition.kind = TypeKind::InputObject;
489 type_definition
490 .input_fields
491 .extend(fields.into_iter().map(|field| {
492 (
493 field.node.name.node.clone(),
494 convert_input_value_definition(field.node),
495 )
496 }));
497 }
498 }
499
500 for directive in definition.directives {
501 match directive.node.name.node.as_str() {
502 "owner" => {
503 if let Some(service) = get_argument_str(&directive.node.arguments, "service") {
504 type_definition.owner = Some(service.node.to_string());
505 }
506 }
507 "key" => {
508 if let Some((fields, service)) =
509 get_argument_str(&directive.node.arguments, "fields")
510 .zip(get_argument_str(&directive.node.arguments, "service"))
511 {
512 if let Some(selection_set) = parse_fields(fields.node)
513 .map(|selection_set| Positioned::new(selection_set, directive.pos))
514 {
515 type_definition
516 .keys
517 .entry(service.node.to_string())
518 .or_default()
519 .push(convert_key_fields(selection_set.node));
520 }
521 }
522 }
523 _ => {}
524 }
525 }
526
527 type_definition
528}
529
530fn convert_field_definition(definition: types::FieldDefinition) -> MetaField {
531 let mut field_definition = MetaField {
532 description: definition.description.map(|description| description.node),
533 name: definition.name.node,
534 arguments: definition
535 .arguments
536 .into_iter()
537 .map(|arg| {
538 (
539 arg.node.name.node.clone(),
540 convert_input_value_definition(arg.node),
541 )
542 })
543 .collect(),
544 ty: definition.ty.node,
545 deprecation: get_deprecated(&definition.directives),
546 service: None,
547 requires: None,
548 provides: None,
549 };
550
551 for directive in definition.directives {
552 match directive.node.name.node.as_str() {
553 "resolve" => {
554 if let Some(service) = get_argument_str(&directive.node.arguments, "service") {
555 field_definition.service = Some(service.node.to_string());
556 }
557 }
558 "requires" => {
559 if let Some(fields) = get_argument_str(&directive.node.arguments, "fields") {
560 field_definition.requires = parse_fields(fields.node).map(convert_key_fields);
561 }
562 }
563 "provides" => {
564 if let Some(fields) = get_argument_str(&directive.node.arguments, "fields") {
565 field_definition.provides = parse_fields(fields.node).map(convert_key_fields);
566 }
567 }
568 _ => {}
569 }
570 }
571
572 field_definition
573}
574
575fn convert_key_fields(selection_set: SelectionSet) -> KeyFields {
576 KeyFields(
577 selection_set
578 .items
579 .into_iter()
580 .filter_map(|field| {
581 if let Selection::Field(field) = field.node {
582 Some((
583 field.node.name.node,
584 convert_key_fields(field.node.selection_set.node),
585 ))
586 } else {
587 None
588 }
589 })
590 .collect(),
591 )
592}
593
594fn convert_input_value_definition(arg: parser::types::InputValueDefinition) -> MetaInputValue {
595 MetaInputValue {
596 description: arg.description.map(|description| description.node),
597 name: arg.name.node,
598 ty: arg.ty.node,
599 default_value: arg.default_value.map(|default_value| default_value.node),
600 }
601}
602
603fn convert_directive_definition(directive_definition: DirectiveDefinition) -> MetaDirective {
604 MetaDirective {
605 name: directive_definition.name.node,
606 description: directive_definition
607 .description
608 .map(|directive_definition| directive_definition.node),
609 locations: directive_definition
610 .locations
611 .into_iter()
612 .map(|location| location.node)
613 .collect(),
614 arguments: directive_definition
615 .arguments
616 .into_iter()
617 .map(|arg| {
618 (
619 arg.node.name.node.clone(),
620 convert_input_value_definition(arg.node),
621 )
622 })
623 .collect(),
624 }
625}
626
627fn get_deprecated(directives: &[Positioned<ConstDirective>]) -> Deprecation {
628 directives
629 .iter()
630 .find(|directive| directive.node.name.node.as_str() == "deprecated")
631 .map(|directive| Deprecation::Deprecated {
632 reason: get_argument_str(&directive.node.arguments, "reason")
633 .map(|reason| reason.node.to_string()),
634 })
635 .unwrap_or(Deprecation::NoDeprecated)
636}
637
638fn has_directive(directives: &[Positioned<ConstDirective>], name: &str) -> bool {
639 directives
640 .iter()
641 .any(|directive| directive.node.name.node.as_str() == name)
642}
643
644fn finish_schema(composed_schema: &mut ComposedSchema) {
645 for definition in parser::parse_schema(include_str!("builtin.graphql"))
646 .unwrap()
647 .definitions
648 .into_iter()
649 {
650 match definition {
651 TypeSystemDefinition::Type(type_definition) => {
652 let type_definition = convert_type_definition(type_definition.node);
653 composed_schema
654 .types
655 .insert(type_definition.name.clone(), type_definition);
656 }
657 TypeSystemDefinition::Directive(directive_definition) => {
658 composed_schema.directives.insert(
659 directive_definition.node.name.node.clone(),
660 convert_directive_definition(directive_definition.node),
661 );
662 }
663 TypeSystemDefinition::Schema(_) => {}
664 }
665 }
666
667 if let Some(query_type) = composed_schema.types.get_mut(
668 composed_schema
669 .query_type
670 .as_ref()
671 .map(|name| name.as_str())
672 .unwrap_or("Query"),
673 ) {
674 let name = Name::new("__type");
675 query_type.fields.insert(
676 name.clone(),
677 MetaField {
678 description: None,
679 name,
680 arguments: {
681 let mut arguments = IndexMap::new();
682 let name = Name::new("name");
683 arguments.insert(
684 name.clone(),
685 MetaInputValue {
686 description: None,
687 name,
688 ty: Type::new("String!").unwrap(),
689 default_value: None,
690 },
691 );
692 arguments
693 },
694 ty: Type::new("__Type").unwrap(),
695 deprecation: Deprecation::NoDeprecated,
696 service: None,
697 requires: None,
698 provides: None,
699 },
700 );
701
702 let name = Name::new("__schema");
703 query_type.fields.insert(
704 name.clone(),
705 MetaField {
706 description: None,
707 name,
708 arguments: Default::default(),
709 ty: Type::new("__Schema!").unwrap(),
710 deprecation: Deprecation::NoDeprecated,
711 service: None,
712 requires: None,
713 provides: None,
714 },
715 );
716 }
717
718 let mut possible_types: HashMap<Name, IndexSet<Name>> = Default::default();
719 for ty in composed_schema.types.values() {
720 if ty.kind == TypeKind::Object {
721 for implement in &ty.implements {
722 possible_types
723 .entry(implement.clone())
724 .or_default()
725 .insert(ty.name.clone());
726 }
727 }
728 }
729 for (name, types) in possible_types {
730 if let Some(ty) = composed_schema.types.get_mut(&name) {
731 ty.possible_types = types;
732 }
733 }
734}