From 16efe710c08e1a2ce6757139673749fd8bbe46ae Mon Sep 17 00:00:00 2001 From: smkhalsa Date: Mon, 15 Nov 2021 20:05:34 -0800 Subject: [PATCH] generate possibleTypes map --- codegen/gql_code_builder/lib/src/schema.dart | 8 +- .../lib/src/schema/possible_types.dart | 85 +++++++++++++++++++ 2 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 codegen/gql_code_builder/lib/src/schema/possible_types.dart diff --git a/codegen/gql_code_builder/lib/src/schema.dart b/codegen/gql_code_builder/lib/src/schema.dart index aa9eb78b0..8c66da257 100644 --- a/codegen/gql_code_builder/lib/src/schema.dart +++ b/codegen/gql_code_builder/lib/src/schema.dart @@ -3,6 +3,7 @@ import "package:gql/ast.dart"; import "./schema/enum.dart"; import "./schema/input.dart"; +import "./schema/possible_types.dart"; import "./schema/scalar.dart"; import "../schema.dart"; import "../source.dart"; @@ -49,9 +50,10 @@ class _SchemaBuilderVisitor extends SimpleVisitor { DocumentNode node, ) => Library( - (b) => b.body.addAll( - _acceptMany(node.definitions).whereType(), - ), + (b) => b.body.addAll([ + buildPossibleTypes(node.definitions), + ..._acceptMany(node.definitions).whereType(), + ]), ); @override diff --git a/codegen/gql_code_builder/lib/src/schema/possible_types.dart b/codegen/gql_code_builder/lib/src/schema/possible_types.dart new file mode 100644 index 000000000..109227c85 --- /dev/null +++ b/codegen/gql_code_builder/lib/src/schema/possible_types.dart @@ -0,0 +1,85 @@ +import "package:code_builder/code_builder.dart"; +import "package:gql/ast.dart"; + +Expression buildPossibleTypes( + List definitions, +) { + final typeDefs = definitions.whereType(); + + final typesByName = typeDefs.fold>( + {}, (map, typeDef) => map..[typeDef.name] = typeDef); + + final interfaceImplementors = _interfaceImplementors(typeDefs); + + final Map> possibleTypesMap = + typeDefs.fold({}, (map, typeDef) { + if (typeDef is UnionTypeDefinitionNode || + typeDef is InterfaceTypeDefinitionNode) { + final concreteTypes = _concreteTypes( + typeDef.name, + typesByName, + interfaceImplementors, + ); + return map + ..[typeDef.name.value] = + concreteTypes.map((type) => type.name.value).toSet(); + } else { + return map; + } + }); + + return literalMap(possibleTypesMap).assignConst("possibleTypes"); +} + +Set _concreteTypes( + NameNode name, + Map typesByName, + Map> interfaceImplementors, +) { + final type = typesByName[name]!; + + if (type is UnionTypeDefinitionNode) { + return type.types + .expand( + (subtype) => + _concreteTypes(subtype.name, typesByName, interfaceImplementors), + ) + .toSet(); + } else if (type is InterfaceTypeDefinitionNode) { + return interfaceImplementors[type.name]! + .expand( + (subtype) => + _concreteTypes(subtype.name, typesByName, interfaceImplementors), + ) + .toSet(); + } else if (type is ObjectTypeDefinitionNode) { + return {type}; + } else { + return {}; + } +} + +Map> _interfaceImplementors( + Iterable typeDefs, +) { + final interfaces = typeDefs + .whereType() + .map((interface) => interface.name); + final interfaceMap = Map>.fromIterable( + interfaces, + value: (dynamic _) => {}); + + for (final typeDef in typeDefs) { + if (typeDef is InterfaceTypeDefinitionNode) { + /// TODO: handle interfaces that implement other interfaces once this is implemented in gql ast + /// https://github.com/graphql/graphql-spec/pull/373 + } else if (typeDef is ObjectTypeDefinitionNode) { + typeDef.interfaces + .forEach((interface) => interfaceMap[interface.name]!.add(typeDef)); + } else if (typeDef is UnionTypeDefinitionNode) { + /// TODO: handle unions + } + } + + return interfaceMap; +}