1 module graphql.schema.typeconversions;
2 
3 import std.array : empty;
4 import std.algorithm.searching : canFind;
5 import std.conv : to;
6 import std.stdio;
7 import std.traits;
8 import std.meta;
9 import std.typecons : Nullable, nullable;
10 import std.range : ElementEncodingType;
11 
12 import vibe.data.json;
13 
14 import nullablestore;
15 
16 import graphql.schema.types;
17 import graphql.traits;
18 import graphql.uda;
19 import graphql.constants;
20 
21 @safe:
22 
23 template typeToTypeEnum(Type) {
24 	static if(isAggregateType!Type && hasUDA!(Type, GQLDUdaData)
25 			&& getUDAs!(Type, GQLDUdaData)[0].typeKind != TypeKind.UNDEFINED)
26 	{
27 		enum udas = getUDAs!(Type, GQLDUdaData);
28 		static assert(udas.length == 1);
29 		enum GQLDUdaData u = udas[0];
30 		enum typeToTypeEnum = to!string(u.typeKind);
31 	} else static if(is(Type == enum)) {
32 		enum typeToTypeEnum = "ENUM";
33 	} else static if(is(Type == bool)) {
34 		enum typeToTypeEnum = "SCALAR";
35 	} else static if(is(Type : GQLDCustomLeaf!F, F)) {
36 		enum typeToTypeEnum = "SCALAR";
37 	} else static if(isFloatingPoint!(Type)) {
38 		enum typeToTypeEnum = "SCALAR";
39 	} else static if(isIntegral!(Type)) {
40 		enum typeToTypeEnum = "SCALAR";
41 	} else static if(isSomeString!Type) {
42 		enum typeToTypeEnum = "SCALAR";
43 	} else static if(is(Type == void)) {
44 		enum typeToTypeEnum = "SCALAR";
45 	} else static if(is(Type == union)) {
46 		enum typeToTypeEnum = "UNION";
47 	} else static if(isAggregateType!Type) {
48 		enum typeToTypeEnum = "OBJECT";
49 	} else {
50 		static assert(false, Type.stringof ~ " not handled");
51 	}
52 }
53 
54 template typeToTypeName(Type) {
55 	import graphql.uda : GQLDCustomLeaf;
56 	static if(is(Type == enum)) {
57 		enum typeToTypeName = Type.stringof;
58 	} else static if(is(Type == bool)) {
59 		enum typeToTypeName = "Boolean";
60 	} else static if(is(Type == GQLDCustomLeaf!Fs, Fs...)) {
61 		enum typeToTypeName = Fs[0].stringof;
62 	} else static if(isFloatingPoint!(Type)) {
63 		enum typeToTypeName = "Float";
64 	} else static if(isIntegral!(Type)) {
65 		enum typeToTypeName = "Int";
66 	} else static if(isSomeString!Type) {
67 		enum typeToTypeName = "String";
68 	} else {
69 		enum typeToTypeName = Type.stringof;
70 	}
71 }
72 
73 unittest {
74 	import std.datetime : Date;
75 	static assert(typeToTypeName!(GQLDCustomLeaf!Date) == "Date");
76 }
77 
78 template typeToParameterTypeName(Type) {
79 	template level2(Type) {
80 		static if(is(Type : Nullable!F, F)) {
81 			enum level2 = typeToTypeName!F;
82 		} else {
83 			enum level2 = typeToTypeName!Type ~ "!";
84 		}
85 	}
86 
87 	template level1(Type) {
88 		static if(isArray!Type && !isSomeString!Type) {
89 			enum level1 = "[" ~ level2!(ElementEncodingType!Type) ~ "]";
90 		} else {
91 			enum level1 = typeToTypeName!Type;
92 		}
93 	}
94 
95 	template level0(Type) {
96 		static if(is(Type : Nullable!F, F)) {
97 			enum level0 = level1!F;
98 		} else {
99 			enum level0 = level1!Type ~ "!";
100 		}
101 	}
102 
103 	template levelM1(Type) {
104 		static if(is(Type : NullableStore!F, F)) {
105 			enum levelM1 = level0!F;
106 		} else {
107 			enum levelM1 = level0!Type;
108 		}
109 	}
110 
111 	enum typeToParameterTypeName = levelM1!Type;
112 }
113 
114 unittest {
115 	import std.datetime : Date;
116 	static assert(typeToParameterTypeName!(int) == "Int!");
117 	static assert(typeToParameterTypeName!(Nullable!int) == "Int");
118 	static assert(typeToParameterTypeName!(double) == "Float!");
119 	static assert(typeToParameterTypeName!(Nullable!double) == "Float");
120 	static assert(typeToParameterTypeName!(double[]) == "[Float!]!");
121 	static assert(typeToParameterTypeName!(Nullable!(double)[]) == "[Float]!");
122 	static assert(typeToParameterTypeName!(Nullable!(Nullable!(double)[])) ==
123 			"[Float]");
124 	static assert(typeToParameterTypeName!(GQLDCustomLeaf!Date) == "Date!");
125 }
126 
127 unittest {
128 	enum AEnum {
129 		one,
130 		two,
131 		three
132 	}
133 	static assert(typeToParameterTypeName!(AEnum) == "AEnum!");
134 	static assert(typeToParameterTypeName!(Nullable!AEnum) == "AEnum");
135 	static assert(typeToParameterTypeName!(Nullable!(AEnum)[]) == "[AEnum]!");
136 	static assert(typeToParameterTypeName!(Nullable!(Nullable!(AEnum)[])) ==
137 			"[AEnum]");
138 }
139 
140 template isScalarType(Type) {
141 	static if(is(Type == bool)) {
142 		enum isScalarType = true;
143 	} else static if(is(Type == GQLDCustomLeaf!Fs, Fs...)) {
144 		enum isScalarType = true;
145 	} else static if(isFloatingPoint!(Type)) {
146 		enum isScalarType = true;
147 	} else static if(isIntegral!(Type)) {
148 		enum isScalarType = true;
149 	} else static if(isSomeString!Type) {
150 		enum isScalarType = true;
151 	} else static if(is(Type == enum)) {
152 		enum isScalarType = true;
153 	} else {
154 		enum isScalarType = false;
155 	}
156 }
157 
158 template typeToFieldType(Type) {
159 	static if(isArray!Type && !isSomeString!Type) {
160 		enum typeToFieldType = "__listType";
161 	} else static if(is(Type : Nullable!F, F)) {
162 		enum typeToFieldType = F.stringof;
163 	} else static if(is(Type : NullableStore!F, F)) {
164 		enum typeToFieldType = Type.TypeValue.stringof;
165 	} else {
166 		enum typeToFieldType = "__nonNullType";
167 	}
168 }
169 
170 Json typeFields(T)() {
171 	import graphql.uda;
172 	static enum memsToIgnore = ["__ctor", "toString", "toHash", "opCmp",
173 			"opEquals", "Monitor", "factory"];
174 	Json ret = Json.emptyArray();
175 	bool[string] fieldsAlreadyIn;
176 	alias TplusParents = AliasSeq!(T, InheritedClasses!T);
177 	static foreach(Type; TplusParents) {{
178 		static foreach(mem; __traits(allMembers, Type)) {{
179 			enum GQLDUdaData udaData = getUdaData!(Type, mem);
180 			static if(!canFind(memsToIgnore, mem)
181 					&& udaData.ignore != Ignore.yes)
182 			{
183 				if(mem !in fieldsAlreadyIn) {
184 					fieldsAlreadyIn[mem] = true;
185 					Json tmp = Json.emptyObject();
186 					tmp[Constants.name] = mem;
187 					// needed for interfacesForType
188 					tmp[Constants.__typename] = "__Field";
189 					tmp[Constants.description] =
190 						udaData.description.getText().empty
191 							? Json(null)
192 							: Json(udaData.description.getText());
193 
194 					tmp[Constants.isDeprecated] =
195 						udaData.deprecationInfo.isDeprecated == IsDeprecated.yes
196 							? true
197 							: false;
198 
199 					tmp[Constants.deprecationReason] =
200 						udaData.deprecationInfo.isDeprecated == IsDeprecated.yes
201 							? Json(udaData.deprecationInfo.deprecationReason)
202 							: Json(null);
203 
204 					tmp[Constants.args] = Json.emptyArray();
205 					static if(isCallable!(__traits(getMember, Type, mem))) {
206 						alias RT = ReturnType!(__traits(getMember, Type, mem));
207 						alias RTS = stripArrayAndNullable!RT;
208 						//tmp[Constants.typenameOrig] = typeToTypeName!(RT);
209 						tmp[Constants.typenameOrig] = typeToParameterTypeName!(RT);
210 
211 						// InputValue
212 						alias paraNames = ParameterIdentifierTuple!(
213 								__traits(getMember, Type, mem)
214 							);
215 						alias paraTypes = Parameters!(
216 								__traits(getMember, Type, mem)
217 							);
218 						alias paraDefs = ParameterDefaults!(
219 								__traits(getMember, Type, mem)
220 							);
221 						static foreach(idx; 0 .. paraNames.length) {{
222 							Json iv = Json.emptyObject();
223 							iv[Constants.name] = paraNames[idx];
224 							// needed for interfacesForType
225 							iv[Constants.__typename] = Constants.__InputValue;
226 							iv[Constants.description] = Json(null);
227 							static if(__traits(compiles, __traits(getAttributes,
228 									Parameters!(__traits(getMember, Type,
229 										mem))[idx ..  idx + 1])))
230 							{
231 								iv[Constants.description] = Json(null);
232 								alias udad = __traits(getAttributes,
233 										Parameters!(__traits(getMember, Type,
234 											mem))[idx ..  idx + 1]);
235 								static if(udad.length == 1) {
236 									enum F = udad[0];
237 									static if(is(typeof(F) == GQLDUdaData)) {
238 										iv[Constants.description] =
239 											F.description.text;
240 									}
241 								}
242 							}
243 							iv[Constants.typenameOrig] =
244 								typeToParameterTypeName!(paraTypes[idx]);
245 							static if(!is(paraDefs[idx] == void)) {
246 								iv[Constants.defaultValue] = serializeToJson(
247 										paraDefs[idx]
248 									)
249 									.toString();
250 							} else {
251 								iv[Constants.defaultValue] = Json(null);
252 							}
253 							tmp[Constants.args] ~= iv;
254 						}}
255 					} else {
256 						tmp[Constants.typenameOrig] =
257 							typeToParameterTypeName!(
258 							//typeToTypeName!(
259 								typeof(__traits(getMember, Type, mem))
260 							);
261 					}
262 					ret ~= tmp;
263 				}
264 			}
265 		}}
266 	}}
267 	//writefln("%s %s", __LINE__, ret.toPrettyString());
268 	return ret;
269 }
270 
271 Json inputFields(Type)() {
272 	Json ret = Json.emptyArray();
273 	alias types = FieldTypeTuple!Type;
274 	alias names = FieldNameTuple!Type;
275 	static foreach(idx; 0 .. types.length) {{
276 		enum GQLDUdaData udaData = getUdaData!(types[idx]);
277 		Json tmp = Json.emptyObject();
278 		tmp[Constants.name] = names[idx];
279 		tmp[Constants.description] = udaData.description.getText().empty
280 				? Json(null)
281 				: Json(udaData.description.getText());
282 
283 		// needed for interfacesForType
284 		tmp[Constants.__typename] = Constants.__InputValue;
285 
286 		//tmp[Constants.typenameOrig] = typeToTypeName!(types[idx]);
287 		tmp[Constants.typenameOrig] = typeToParameterTypeName!(types[idx]);
288 		auto t = __traits(getMember, Type.init, names[idx]);
289 		tmp[Constants.defaultValue] = serializeToJson(t);
290 		ret ~= tmp;
291 	}}
292 	return ret;
293 }
294 
295 Json emptyType() {
296 	Json ret = Json.emptyObject();
297 	ret[Constants.name] = Json(null);
298 	ret[Constants.description] = Json(null);
299 	ret[Constants.fields] = Json(null);
300 	ret[Constants.interfacesNames] = Json(null);
301 	ret[Constants.possibleTypesNames] = Json(null);
302 	ret[Constants.enumValues] = Json(null);
303 	ret["ofType"] = Json(null);
304 	return ret;
305 }
306 
307 Json removeNonNullAndList(Json j) {
308 	string t = j["kind"].get!string();
309 	if(t == "NON_NULL" || t == "LIST") {
310 		return removeNonNullAndList(j["ofType"]);
311 	} else {
312 		return j;
313 	}
314 }
315 
316 // remove the top nullable to find out if we have a NON_NULL or not
317 Json typeToJson(Type,Schema)() {
318 	static if(is(Type : Nullable!F, F)) {
319 		return typeToJson1!(F,Schema,Type)();
320 	} else static if(is(Type : NullableStore!F, F)) {
321 		return typeToJson1!(Type.TypeValue,Schema,Type)();
322 	} else {
323 		Json ret = emptyType();
324 		ret["kind"] = "NON_NULL";
325 		ret[Constants.__typename] = "__Type";
326 		ret["ofType"] = typeToJson1!(Type,Schema,Type)();
327 		return ret;
328 	}
329 }
330 
331 // remove the array is present
332 Json typeToJson1(Type,Schema,Orig)() {
333 	static if(isArray!Type && !isSomeString!Type && !is(Type == enum)) {
334 		Json ret = emptyType();
335 		ret["kind"] = "LIST";
336 		ret[Constants.__typename] = "__Type";
337 		ret["ofType"] = typeToJson2!(ElementEncodingType!Type, Schema, Orig)();
338 		return ret;
339 	} else {
340 		return typeToJsonImpl!(Type, Schema, Orig)();
341 	}
342 }
343 
344 // remove another nullable
345 Json typeToJson2(Type,Schema,Orig)() {
346 	static if(is(Type : Nullable!F, F)) {
347 		return typeToJsonImpl!(F, Schema, Orig)();
348 	} else static if(is(Type : NullableStore!F, F)) {
349 		return typeToJsonImpl!(Type.TypeValue, Schema, Orig)();
350 	} else {
351 		Json ret = emptyType();
352 		ret["kind"] = "NON_NULL";
353 		ret[Constants.__typename] = "__Type";
354 		ret["ofType"] = typeToJsonImpl!(Type, Schema, Orig)();
355 		return ret;
356 	}
357 }
358 
359 Json typeToJsonImpl(Type,Schema,Orig)() {
360 	Json ret = Json.emptyObject();
361 	enum string kind = typeToTypeEnum!(stripArrayAndNullable!Type);
362 	ret["kind"] = kind;
363 	ret[Constants.__typename] = "__Type";
364 	ret[Constants.name] = typeToTypeName!Type;
365 
366 	enum GQLDUdaData udaData = getUdaData!(Orig);
367 	enum des = udaData.description.text;
368 	ret[Constants.description] = des.empty
369 			? Json(null)
370 			: Json(des);
371 
372 	ret[Constants.isDeprecated] =
373 		udaData.deprecationInfo.isDeprecated == IsDeprecated.yes
374 			? true
375 			: false;
376 
377 	ret[Constants.deprecationReason] =
378 		udaData.deprecationInfo.isDeprecated == IsDeprecated.yes
379 			? Json(udaData.deprecationInfo.deprecationReason)
380 			: Json(null);
381 
382 	// fields
383 	static if((is(Type == class) || is(Type == interface) || is(Type == struct))
384 			&& !is(Type : Nullable!K, K) && !is(Type : NullableStore!K, K)
385 			&& !is(Type : GQLDCustomLeaf!Ks, Ks...))
386 	{
387 		ret[Constants.fields] = typeFields!Type();
388 	} else {
389 		ret[Constants.fields] = Json(null);
390 	}
391 
392 	// inputFields
393 	static if(kind == Constants.INPUT_OBJECT) {
394 		ret[Constants.inputFields] = inputFields!Type();
395 	} else {
396 		ret[Constants.inputFields] = Json(null);
397 	}
398 
399 	// needed to resolve interfaces
400 	static if(is(Type == class) || is(Type == interface)) {
401 		ret[Constants.interfacesNames] = Json.emptyArray();
402 		static foreach(interfaces; InheritedClasses!Type) {{
403 			ret[Constants.interfacesNames] ~= interfaces.stringof;
404 		}}
405 	} else {
406 		ret[Constants.interfacesNames] = Json(null);
407 	}
408 
409 	// needed to resolve possibleTypes
410 	static if(is(Type == class) || is(Type == union) || is(Type == interface)) {
411 		static if(is(Type == union)) {
412 			ret[Constants.possibleTypesNames] = Json.emptyArray();
413 			static foreach(pt; Filter!(isAggregateType, FieldTypeTuple!Type)) {
414 				ret[Constants.possibleTypesNames] ~= pt.stringof;
415 			}
416 		} else {
417 			import graphql.reflection;
418 			// need to search for all types that we support that are derived
419 			// from this type
420 			ret[Constants.possibleTypesNames] = Json.emptyArray();
421 			foreach(tname;
422 					SchemaReflection!Schema.instance.derivatives.get(typeid(Type),
423 																	 null))
424 			{
425 				ret[Constants.possibleTypesNames] ~= tname;
426 			}
427 		}
428 	} else {
429 		ret[Constants.possibleTypesNames] = Json(null);
430 	}
431 
432 	// enumValues
433 	static if(is(Type == enum)) {
434 		ret[Constants.enumValues] = Json.emptyArray();
435 		static foreach(mem; EnumMembers!Type) {{
436 			Json tmp = Json.emptyObject();
437 			tmp[Constants.__TypeKind] = Constants.__EnumValue;
438 			tmp[Constants.name] = Json(to!string(mem));
439 			tmp[Constants.description] = "ENUM_DESCRIPTION_TODO";
440 			tmp[Constants.isDeprecated] = false;
441 			tmp[Constants.deprecationReason] = "ENUM_DEPRECATIONREASON_TODO";
442 			ret[Constants.enumValues] ~= tmp;
443 		}}
444 	} else {
445 		ret[Constants.enumValues] = Json(null);
446 	}
447 
448 	// needed to resolve ofType
449 	static if(is(Type : Nullable!F, F)) {
450 		ret[Constants.ofTypeName] = F.stringof;
451 	} else static if(is(Type : NullableStore!F, F)) {
452 		ret[Constants.ofTypeName] = F.stringof;
453 	} else static if(is(Type : GQLDCustomLeaf!Fs, Fs...)) {
454 		ret[Constants.ofTypeName] = Fs[0].stringof;
455 	} else static if(isArray!Type) {
456 		ret[Constants.ofTypeName] = ElementEncodingType!(Type).stringof;
457 	}
458 
459 	return ret;
460 }
461 
462 @safe unittest {
463 	import std.format : format;
464 	Json r = typeToJson!(string,void)();
465 	Json e = parseJsonString(`
466 		{
467 			"__typename": "__Type",
468 			"possibleTypesNames": null,
469 			"enumValues": null,
470 			"interfacesNames": null,
471 			"kind": "NON_NULL",
472 			"name": null,
473 			"ofType": {
474 				"__typename": "__Type",
475 				"possibleTypesNames": null,
476 				"enumValues": null,
477 				"interfacesNames": null,
478 				"kind": "SCALAR",
479 				"isDeprecated": false,
480 				"deprecationReason": null,
481 				"name": "String",
482 				"description": null,
483 				"inputFields": null,
484 				"ofTypeName": "immutable(char)",
485 				"fields": null
486 			},
487 			"description": null,
488 			"fields": null
489 		}
490 		`);
491 	assert(r == e, format("exp:\n%s\ngot:\n%s", e.toPrettyString(),
492 				r.toPrettyString()));
493 }
494 
495 @safe unittest {
496 	enum FooBar {
497 		foo,
498 		bar
499 	}
500 
501 	import std.format : format;
502 	Json r = typeToJson!(FooBar,void)();
503 	Json e = parseJsonString(`
504 {
505 	"__typename": "__Type",
506 	"possibleTypesNames": null,
507 	"enumValues": null,
508 	"interfacesNames": null,
509 	"kind": "NON_NULL",
510 	"name": null,
511 	"ofType": {
512 		"__typename": "__Type",
513 		"possibleTypesNames": null,
514 		"enumValues": [
515 			{
516 				"description": "ENUM_DESCRIPTION_TODO",
517 				"deprecationReason": "ENUM_DEPRECATIONREASON_TODO",
518 				"__TypeKind": "__EnumValue",
519 				"isDeprecated": false,
520 				"name": "foo"
521 			},
522 			{
523 				"description": "ENUM_DESCRIPTION_TODO",
524 				"deprecationReason": "ENUM_DEPRECATIONREASON_TODO",
525 				"__TypeKind": "__EnumValue",
526 				"isDeprecated": false,
527 				"name": "bar"
528 			}
529 		],
530 		"interfacesNames": null,
531 		"kind": "ENUM",
532 		"isDeprecated": false,
533 		"deprecationReason": null,
534 		"name": "FooBar",
535 		"description": null,
536 		"inputFields": null,
537 		"fields": null
538 	},
539 	"description": null,
540 	"fields": null
541 }
542 		`);
543 	assert(r == e, format("exp:\n%s\ngot:\n%s", e.toPrettyString(),
544 				r.toPrettyString()));
545 }
546 @safe unittest {
547 	import std.format : format;
548 	Json r = typeToJson!(Nullable!string,void)();
549 	Json e = parseJsonString(`
550 		{
551 			"__typename": "__Type",
552 			"possibleTypesNames": null,
553 			"enumValues": null,
554 			"interfacesNames": null,
555 			"kind": "SCALAR",
556 			"isDeprecated": false,
557 			"deprecationReason": null,
558 			"name": "String",
559 			"description": null,
560 			"inputFields": null,
561 			"ofTypeName": "immutable(char)",
562 			"fields": null
563 		}
564 		`);
565 	assert(r == e, format("exp:\n%s\ngot:\n%s", e.toPrettyString(),
566 				r.toPrettyString()));
567 }
568 
569 @safe unittest {
570 	import std.format : format;
571 	Json r = typeToJson!(Nullable!string,void)();
572 	Json e = parseJsonString(`
573 		{
574 			"__typename": "__Type",
575 			"possibleTypesNames": null,
576 			"enumValues": null,
577 			"interfacesNames": null,
578 			"kind": "SCALAR",
579 			"isDeprecated": false,
580 			"deprecationReason": null,
581 			"name": "String",
582 			"description": null,
583 			"inputFields": null,
584 			"ofTypeName": "immutable(char)",
585 			"fields": null
586 		}
587 		`);
588 	assert(r == e, format("exp:\n%s\ngot:\n%s", e.toPrettyString(),
589 				r.toPrettyString()));
590 }
591 
592 Json directivesToJson(Directives)() {
593 	import std.string : stripLeft;
594 	Json ret = Json.emptyArray();
595 	static enum memsToIgnore = ["__ctor", "toString", "toHash", "opCmp",
596 			"opEquals", "Monitor", "factory"];
597 	alias TplusParents = AliasSeq!(Directives, InheritedClasses!Directives);
598 	static foreach(Type; TplusParents) {{
599 		static foreach(mem; __traits(allMembers, Type)) {{
600 			static if(!canFind(memsToIgnore, mem)) {
601 				Json tmp = Json.emptyObject();
602 				enum GQLDUdaData udaData = getUdaData!(Type, mem);
603 				tmp[Constants.name] = mem;
604 				// needed for interfacesForType
605 				tmp[Constants.__typename] = Constants.__Directive;
606 				tmp[Constants.description] = udaData.description.getText().empty
607 						? Json(null)
608 						: Json(udaData.description.text);
609 
610 				tmp[Constants.isDeprecated] =
611 					udaData.deprecationInfo.isDeprecated == IsDeprecated.yes
612 						? true
613 						: false;
614 
615 				tmp[Constants.deprecationReason] =
616 					udaData.deprecationInfo.isDeprecated == IsDeprecated.yes
617 						? Json(udaData.deprecationInfo.deprecationReason)
618 						: Json(null);
619 
620 				//tmp[Constants.description] = Json(null);
621 				tmp[Constants.locations] = Json.emptyArray();
622 				tmp[Constants.args] = Json.emptyArray();
623 				static if(isCallable!(__traits(getMember, Type, mem))) {
624 					// InputValue
625 					alias paraNames = ParameterIdentifierTuple!(
626 							__traits(getMember, Type, mem)
627 						);
628 					alias paraTypes = Parameters!(
629 							__traits(getMember, Type, mem)
630 						);
631 					alias paraDefs = ParameterDefaults!(
632 							__traits(getMember, Type, mem)
633 						);
634 					static foreach(idx; 0 .. paraNames.length) {{
635 						Json iv = Json.emptyObject();
636 						// TODO remove the strip left. Its in because the
637 						// two default directives of GraphQL skip and include
638 						// both have one parameter named "if".
639 						iv[Constants.name] = stripLeft(paraNames[idx], "_");
640 						iv[Constants.description] = Json(null);
641 						// needed for interfacesForType
642 						iv[Constants.__typename] = Constants.__InputValue;
643 						iv[Constants.typenameOrig] = typeToTypeName!(paraTypes[idx]);
644 						//iv[Constants.typenameOrig] = typeToParameterTypeName!(paraTypes[idx]);
645 						static if(!is(paraDefs[idx] == void)) {
646 							iv[Constants.defaultValue] = serializeToJson(paraDefs[idx])
647 								.toString();
648 						} else {
649 							iv[Constants.defaultValue] = Json(null);
650 						}
651 						tmp[Constants.args] ~= iv;
652 					}}
653 				}
654 				ret ~= tmp;
655 			}
656 		}}
657 	}}
658 	return ret;
659 }
660 
661 Json getField(Json j, string name) {
662 	import graphql.constants;
663 
664 	if(j.type != Json.Type.object || Constants.fields !in j
665 			|| j[Constants.fields].type != Json.Type.array)
666 	{
667 		return Json.init;
668 	}
669 
670 	foreach(it; j[Constants.fields].byValue) {
671 		string itName = it[Constants.name].get!string();
672 		if(itName == name) {
673 			return it;
674 		}
675 	}
676 	return Json.init;
677 }
678 
679 Json getIntrospectionField(string name) {
680 	import std.format : format;
681 	Json ret = Json.emptyObject();
682 	ret[Constants.typenameOrig] = name == Constants.__typename
683 		? "String"
684 		: name == Constants.__schema
685 			? "__Schema"
686 			: name == Constants.__type
687 				? "__Type"
688 				: format("Not known introspection name '%s'", name);
689 	return ret;
690 }