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