-
Notifications
You must be signed in to change notification settings - Fork 66
/
TypeProvider.swift
252 lines (214 loc) · 8.47 KB
/
TypeProvider.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
import GraphQL
protocol TypeProvider: AnyObject {
var graphQLNameMap: [AnyType: String] { get set }
var graphQLTypeMap: [AnyType: GraphQLType] { get set }
}
extension TypeProvider {
func contains(type: Any.Type) -> Bool {
graphQLTypeMap[AnyType(type)] != nil
}
func mapName(_ type: Any.Type, to name: String) throws {
guard !(type is Void.Type) else {
return
}
let key = AnyType(type)
guard graphQLNameMap[key] == nil else {
throw GraphQLError(
message: "Duplicate type registration for GraphQL type name \"\(name)\" while trying to register type \(Reflection.name(for: type))"
)
}
graphQLNameMap[key] = name
}
func map(_ type: Any.Type, to graphQLType: GraphQLType) throws {
guard !(type is Void.Type) else {
return
}
let key = AnyType(type)
guard graphQLTypeMap[key] == nil else {
throw GraphQLError(
message: "Duplicate type registration for GraphQLType \"\(graphQLType.debugDescription)\" while trying to register type \(Reflection.name(for: type))"
)
}
graphQLTypeMap[key] = graphQLType
}
func getGraphQLOptionalType(from type: GraphQLType, isOptional: Bool) throws -> GraphQLType {
if isOptional {
return type
} else if let type = type as? GraphQLNullableType {
return GraphQLNonNull(type)
} else {
throw GraphQLError(
message:
"GraphQLType \"\(type)\" is not nullable."
)
}
}
func getGraphQLType(from type: Any.Type, isOptional: Bool = false) throws -> GraphQLType {
if let type = type as? Wrapper.Type {
switch type.modifier {
case .optional:
return try getGraphQLType(from: type.wrappedType, isOptional: true)
case .list:
let graphQLType = try getGraphQLType(from: type.wrappedType)
return try getGraphQLOptionalType(
from: GraphQLList(graphQLType),
isOptional: isOptional
)
case .reference:
let name = getGraphQLName(of: type.wrappedType)
let referenceType = GraphQLTypeReference(name)
return try getGraphQLOptionalType(from: referenceType, isOptional: isOptional)
}
} else {
if let graphQLType = graphQLTypeMap[AnyType(type)] {
return try getGraphQLOptionalType(from: graphQLType, isOptional: isOptional)
} else {
// If we haven't seen this type yet, just store it as a type reference and resolve later.
let name = getGraphQLName(of: type)
let referenceType = GraphQLTypeReference(name)
return try getGraphQLOptionalType(from: referenceType, isOptional: isOptional)
}
}
}
func getOutputType(from type: Any.Type, field: String) throws -> GraphQLOutputType {
let graphQLType: GraphQLType
do {
graphQLType = try getGraphQLType(from: type)
} catch {
throw GraphQLError(
message:
// TODO: Add field type and use "type.field" format.
"Cannot use type \"\(type)\" for field \"\(field)\". " +
"Type does not map to a GraphQL type.",
originalError: error
)
}
guard let outputType = graphQLType as? GraphQLOutputType else {
throw GraphQLError(
message:
// TODO: Add field type and use "type.field" format.
"Cannot use type \"\(type)\" for field \"\(field)\". " +
"Mapped GraphQL type is not an output type."
)
}
return outputType
}
func getInputType(from type: Any.Type, field: String) throws -> GraphQLInputType {
let graphQLType: GraphQLType
do {
graphQLType = try getGraphQLType(from: type)
} catch {
throw GraphQLError(
message:
// TODO: Add field type and use "type.field" format.
"Cannot use type \"\(type)\" for field \"\(field)\". " +
"Type does not map to a GraphQL type.",
originalError: error
)
}
guard let inputType = graphQLType as? GraphQLInputType else {
throw GraphQLError(
message:
// TODO: Add field type and use "type.field" format.
"Cannot use type \"\(type)\" for field \"\(field)\". " +
"Mapped GraphQL type is not an input type."
)
}
return inputType
}
func getNamedType(from type: Any.Type) throws -> GraphQLNamedType {
let graphQLType: GraphQLType
do {
graphQLType = try getGraphQLType(from: type)
} catch {
throw GraphQLError(
message:
"Cannot use type \"\(type)\" as named type. " +
"Type does not map to a GraphQL type.",
originalError: error
)
}
guard let namedType = GraphQL.getNamedType(type: graphQLType) else {
throw GraphQLError(
message:
"Cannot use type \"\(type)\" as named type. " +
"Mapped GraphQL type is not a named type."
)
}
return namedType
}
func getInterfaceType(from type: Any.Type) throws -> GraphQLInterfaceType {
// TODO: Remove this when Reflection error is fixed
guard Reflection.isProtocol(type: type) else {
throw GraphQLError(
message:
// TODO: Add more information of where the error happened.
"Cannot use type \"\(type)\" as interface. " +
"Type is not a protocol."
)
}
let graphQLType: GraphQLType
do {
graphQLType = try getGraphQLType(from: type)
} catch {
throw GraphQLError(
message:
// TODO: Add more information of where the error happened.
"Cannot use type \"\(type)\" as interface. " +
"Type does not map to a GraphQL type.",
originalError: error
)
}
guard let nonNull = graphQLType as? GraphQLNonNull else {
throw GraphQLError(
message:
// TODO: Add more information of where the error happened.
"Cannot use type \"\(type)\" as interface. " +
"Mapped GraphQL type is nullable."
)
}
guard let interfaceType = nonNull.ofType as? GraphQLInterfaceType else {
throw GraphQLError(
message:
// TODO: Add more information of where the error happened.
"Cannot use type \"\(type)\" as interface. " +
"Mapped GraphQL type is not an interface type."
)
}
return interfaceType
}
func getObjectType(from type: Any.Type) throws -> GraphQLObjectType {
let graphQLType: GraphQLType
do {
graphQLType = try getGraphQLType(from: type)
} catch {
throw GraphQLError(
message:
// TODO: Add more information of where the error happened.
"Cannot use type \"\(type)\" as object. " +
"Type does not map to a GraphQL type.",
originalError: error
)
}
guard let nonNull = graphQLType as? GraphQLNonNull else {
throw GraphQLError(
message:
// TODO: Add more information of where the error happened.
"Cannot use type \"\(type)\" as object. " +
"Mapped GraphQL type is nullable."
)
}
guard let objectType = nonNull.ofType as? GraphQLObjectType else {
throw GraphQLError(
message:
// TODO: Add more information of where the error happened.
"Cannot use type \"\(type)\" as object. " +
"Mapped GraphQL type is not an object type."
)
}
return objectType
}
private func getGraphQLName(of type: Any.Type) -> String {
return graphQLNameMap[AnyType(type)] ?? Reflection.name(for: type)
}
}