1 module graphql.uda;
2 
3 import std.traits : isBuiltinType;
4 
5 @safe:
6 
7 enum TypeKind {
8 	UNDEFINED,
9 	SCALAR,
10 	OBJECT,
11 	INTERFACE,
12 	UNION,
13 	ENUM,
14 	INPUT_OBJECT,
15 	LIST,
16 	NON_NULL
17 }
18 
19 struct GQLDUdaData {
20 	TypeKind typeKind;
21 	GQLDDeprecatedData deprecationInfo;
22 	GQLDDescription description;
23 	Ignore ignore;
24 	IgnoreForInput ignoreForInput;
25 	RequiredForInput requiredForInput;
26 }
27 
28 enum IgnoreForInput {
29 	undefined,
30 	yes,
31 	no
32 }
33 
34 enum RequiredForInput {
35 	undefined,
36 	yes,
37 	no
38 }
39 
40 enum Ignore {
41 	undefined,
42 	yes,
43 	no
44 }
45 
46 enum IsDeprecated {
47 	undefined,
48 	yes,
49 	no
50 }
51 
52 string toStringImpl(T)(T t) {
53 	static if(__traits(hasMember, T, "toString")) {
54 		return t.toString();
55 	} else {
56 		import std.format : format;
57 		return format("%s", t);
58 	}
59 }
60 
61 /* The wrapped
62 	T = the wrapped type
63 	SerializationFun = the function to use to serialize T
64 */
65 struct GQLDCustomLeaf(T, alias SerializationFun = toStringImpl!T) {
66 	alias Type = T;
67 	Type value;
68 	alias value this;
69 
70 	alias Fun = SerializationFun;
71 
72 	this(Type value) {
73 		this.value = value;
74 	}
75 
76 	void opAssign(Type value) {
77 		this.value = value;
78 	}
79 }
80 
81 unittest {
82 	import std.datetime : DateTime;
83 	import vibe.data.json;
84 
85 	Json fun(DateTime dt) {
86 		return Json(dt.toISOExtString());
87 	}
88 
89 	auto f = GQLDCustomLeaf!DateTime();
90 
91 	GQLDCustomLeaf!DateTime dt = DateTime(1337, 1, 1);
92 }
93 
94 unittest {
95 	import std.typecons : Nullable, nullable;
96 	import std.datetime : DateTime;
97 	Nullable!(GQLDCustomLeaf!DateTime) dt;
98 }
99 
100 struct GQLDDeprecatedData {
101 	IsDeprecated isDeprecated;
102 	string deprecationReason;
103 }
104 
105 struct GQLDDescription {
106 	string text;
107 }
108 
109 GQLDDeprecatedData GQLDDeprecated(IsDeprecated isDeprecated) {
110 	return GQLDDeprecatedData(isDeprecated, "");
111 }
112 
113 GQLDDeprecatedData GQLDDeprecated(IsDeprecated isDeprecated,
114 		string deprecationReason)
115 {
116 	return GQLDDeprecatedData(isDeprecated, deprecationReason);
117 }
118 
119 GQLDUdaData GQLDUda(Args...)(Args args) {
120 	GQLDUdaData ret;
121 	static foreach(mem; __traits(allMembers, GQLDUdaData)) {
122 		static foreach(arg; args) {
123 			static if(is(typeof(__traits(getMember, ret, mem)) ==
124 						typeof(arg)))
125 			{
126 				__traits(getMember, ret, mem) = arg;
127 			}
128 		}
129 	}
130 	return ret;
131 }
132 
133 private template isGQLDUdaData(alias Type) {
134 	enum isGQLDUdaData = is(typeof(Type) == GQLDUdaData);
135 }
136 
137 private template getGQLDUdaData(Type, string mem) {
138 	import std.meta : Filter;
139 	alias getGQLDUdaData =
140 		Filter!(isGQLDUdaData,
141 				__traits(getAttributes, __traits(getMember, Type, mem)));
142 }
143 
144 private template getGQLDUdaData(Type) {
145 	import std.meta : Filter;
146 	alias getGQLDUdaData =
147 		Filter!(isGQLDUdaData, __traits(getAttributes, Type));
148 }
149 
150 template getUdaData(Type) {
151 	static if(isBuiltinType!Type) {
152 		enum getUdaData = GQLDUdaData.init;
153 	} else {
154 		alias GQLDUdaDataAS = getGQLDUdaData!Type;
155 		static if(GQLDUdaDataAS.length > 0) {
156 			enum getUdaData = GQLDUdaDataAS[0];
157 		} else {
158 			enum getUdaData = GQLDUdaData.init;
159 		}
160 	}
161 }
162 
163 template getUdaData(Type, string mem) {
164 	alias GQLDUdaDataAS = getGQLDUdaData!(Type, mem);
165 	static if(GQLDUdaDataAS.length > 0) {
166 		enum  getUdaData = GQLDUdaDataAS[0];
167 	} else {
168 		enum  getUdaData = GQLDUdaData.init;
169 	}
170 }
171 
172 unittest {
173 	@GQLDUda(
174 		GQLDDeprecated(IsDeprecated.yes, "Stupid name"),
175 		GQLDDescription("You normal test struct")
176 	)
177 	struct Foo {
178 		@GQLDUda(
179 			GQLDDeprecated(IsDeprecated.no, "Very good name"),
180 			GQLDDescription("Contains something")
181 		)
182 		int bar;
183 
184 		int args;
185 	}
186 
187 	enum GQLDUdaData fd = getUdaData!Foo;
188 	static assert(fd.deprecationInfo.isDeprecated == IsDeprecated.yes);
189 	static assert(fd.deprecationInfo.deprecationReason == "Stupid name");
190 	static assert(fd.description.text == "You normal test struct");
191 
192 	enum GQLDUdaData bd = getUdaData!(Foo, "bar");
193 	static assert(bd.deprecationInfo.isDeprecated == IsDeprecated.no);
194 	static assert(bd.deprecationInfo.deprecationReason == "Very good name");
195 	static assert(bd.description.text == "Contains something");
196 
197 	enum GQLDUdaData ad = getUdaData!(Foo, "args");
198 	static assert(ad.deprecationInfo.isDeprecated == IsDeprecated.undefined);
199 	static assert(ad.deprecationInfo.deprecationReason == "");
200 	static assert(ad.description.text == "");
201 }