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