1 /**
2 	Utility templates that help working with User Defined Attributes
3 
4 	Copyright: © 2013 Sönke Ludwig
5 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6 	Authors: Sönke Ludwig, Михаил Страшун
7 */
8 
9 module dutils.data.utils.uda;
10 
11 /**
12 	Small convenience wrapper to find and extract certain UDA from given type.
13 	Will stop on first element which is of required type.
14 
15 	Params:
16 		UDA = type or template to search for in UDA list
17 		Symbol = symbol to query for UDA's
18 		allow_types = if set to `false` considers attached `UDA` types an error
19 			(only accepts instances/values)
20 
21 	Returns: aggregated search result struct with 3 field. `value` aliases found UDA.
22 		`found` is boolean flag for having a valid find. `index` is integer index in
23 		attribute list this UDA was found at.
24 */
25 template findFirstUDA(alias UDA, alias Symbol, bool allow_types = false)
26 		if (!is(UDA)) {
27 	enum findFirstUDA = findNextUDA!(UDA, Symbol, 0, allow_types);
28 }
29 
30 /// Ditto
31 template findFirstUDA(UDA, alias Symbol, bool allow_types = false) {
32 	enum findFirstUDA = findNextUDA!(UDA, Symbol, 0, allow_types);
33 }
34 
35 private struct UdaSearchResult(alias UDA) {
36 	alias value = UDA;
37 	bool found = false;
38 	long index = -1;
39 }
40 
41 /**
42 	Small convenience wrapper to find and extract certain UDA from given type.
43 	Will start at the given index and stop on the next element which is of required type.
44 
45 	Params:
46 		UDA = type or template to search for in UDA list
47 		Symbol = symbol to query for UDA's
48 		idx = 0-based index to start at. Should be positive, and under the total number of attributes.
49 		allow_types = if set to `false` considers attached `UDA` types an error
50 			(only accepts instances/values)
51 
52 	Returns: aggregated search result struct with 3 field. `value` aliases found UDA.
53 		`found` is boolean flag for having a valid find. `index` is integer index in
54 		attribute list this UDA was found at.
55  */
56 private template findNextUDA(alias UDA, alias Symbol, long idx, bool allow_types = false)
57 		if (!is(UDA)) {
58 	import std.traits : isInstanceOf;
59 	import std.typetuple : TypeTuple;
60 
61 	private alias udaTuple = TypeTuple!(__traits(getAttributes, Symbol));
62 
63 	static assert(idx >= 0, "Index given to findNextUDA can't be negative");
64 	static assert(idx <= udaTuple.length,
65 			"Index given to findNextUDA is above the number of attribute");
66 
67 	public template extract(size_t index, list...) {
68 		static if (!list.length)
69 			enum extract = UdaSearchResult!(null)(false, -1);
70 		else {
71 			static if (is(list[0])) {
72 				static if (is(UDA) && is(list[0] == UDA) || !is(UDA) && isInstanceOf!(UDA, list[0])) {
73 					static assert(allow_types, "findNextUDA is designed to look up values, not types");
74 					enum extract = UdaSearchResult!(list[0])(true, index);
75 				} else
76 					enum extract = extract!(index + 1, list[1 .. $]);
77 			} else {
78 				static if (is(UDA) && is(typeof(list[0]) == UDA) || !is(UDA)
79 						&& isInstanceOf!(UDA, typeof(list[0]))) {
80 					import dutils.data.utils.traits : isPropertyGetter;
81 
82 					static if (isPropertyGetter!(list[0])) {
83 						enum value = list[0];
84 						enum extract = UdaSearchResult!(value)(true, index);
85 					} else
86 						enum extract = UdaSearchResult!(list[0])(true, index);
87 				} else
88 					enum extract = extract!(index + 1, list[1 .. $]);
89 			}
90 		}
91 	}
92 
93 	enum findNextUDA = extract!(idx, udaTuple[idx .. $]);
94 }
95 /// ditto
96 private template findNextUDA(UDA, alias Symbol, long idx, bool allow_types = false) {
97 	import std.traits : isInstanceOf;
98 	import std.typetuple : TypeTuple;
99 
100 	private alias udaTuple = TypeTuple!(__traits(getAttributes, Symbol));
101 
102 	static assert(idx >= 0, "Index given to findNextUDA can't be negative");
103 	static assert(idx <= udaTuple.length,
104 			"Index given to findNextUDA is above the number of attribute");
105 
106 	public template extract(size_t index, list...) {
107 		static if (!list.length)
108 			enum extract = UdaSearchResult!(null)(false, -1);
109 		else {
110 			static if (is(list[0])) {
111 				static if (is(list[0] == UDA)) {
112 					static assert(allow_types, "findNextUDA is designed to look up values, not types");
113 					enum extract = UdaSearchResult!(list[0])(true, index);
114 				} else
115 					enum extract = extract!(index + 1, list[1 .. $]);
116 			} else {
117 				static if (is(typeof(list[0]) == UDA)) {
118 					import dutils.data.utils.traits : isPropertyGetter;
119 
120 					static if (isPropertyGetter!(list[0])) {
121 						enum value = list[0];
122 						enum extract = UdaSearchResult!(value)(true, index);
123 					} else
124 						enum extract = UdaSearchResult!(list[0])(true, index);
125 				} else
126 					enum extract = extract!(index + 1, list[1 .. $]);
127 			}
128 		}
129 	}
130 
131 	enum findNextUDA = extract!(idx, udaTuple[idx .. $]);
132 }
133 
134 ///
135 unittest {
136 	struct Attribute {
137 		int x;
138 	}
139 
140 	@("something", Attribute(42), Attribute(41))
141 	void symbol();
142 
143 	enum result0 = findNextUDA!(string, symbol, 0);
144 	static assert(result0.found);
145 	static assert(result0.index == 0);
146 	static assert(result0.value == "something");
147 
148 	enum result1 = findNextUDA!(Attribute, symbol, 0);
149 	static assert(result1.found);
150 	static assert(result1.index == 1);
151 	static assert(result1.value == Attribute(42));
152 
153 	enum result2 = findNextUDA!(int, symbol, 0);
154 	static assert(!result2.found);
155 
156 	enum result3 = findNextUDA!(Attribute, symbol, result1.index + 1);
157 	static assert(result3.found);
158 	static assert(result3.index == 2);
159 	static assert(result3.value == Attribute(41));
160 }
161 
162 unittest {
163 	struct Attribute {
164 		int x;
165 	}
166 
167 	@(Attribute) void symbol();
168 
169 	static assert(!is(findNextUDA!(Attribute, symbol, 0)));
170 
171 	enum result0 = findNextUDA!(Attribute, symbol, 0, true);
172 	static assert(result0.found);
173 	static assert(result0.index == 0);
174 	static assert(is(result0.value == Attribute));
175 }
176 
177 unittest {
178 	struct Attribute {
179 		int x;
180 	}
181 
182 	enum Dummy;
183 
184 	@property static Attribute getter() {
185 		return Attribute(42);
186 	}
187 
188 	@Dummy @getter void symbol();
189 
190 	enum result0 = findNextUDA!(Attribute, symbol, 0);
191 	static assert(result0.found);
192 	static assert(result0.index == 1);
193 	static assert(result0.value == Attribute(42));
194 }
195 
196 /// Eager version of findNextUDA that represent all instances of UDA in a Tuple.
197 /// If one of the attribute is a type instead of an instance, compilation will fail.
198 template UDATuple(alias UDA, alias Sym) {
199 	import std.typetuple : TypeTuple;
200 
201 	private template extract(size_t maxSize, Founds...) {
202 		private alias LastFound = Founds[$ - 1];
203 		// No more to find
204 		static if (!LastFound.found)
205 			enum extract = Founds[0 .. $ - 1];
206 		else {
207 			// For ease of use, this is a Tuple of UDA, not a tuple of UdaSearchResult!(...)
208 			private alias Result = TypeTuple!(Founds[0 .. $ - 1], LastFound.value);
209 			// We're at the last parameter
210 			static if (LastFound.index == maxSize)
211 				enum extract = Result;
212 			else
213 				enum extract = extract!(maxSize, Result, findNextUDA!(UDA, Sym, LastFound.index + 1));
214 		}
215 	}
216 
217 	private enum maxIndex = TypeTuple!(__traits(getAttributes, Sym)).length;
218 	enum UDATuple = extract!(maxIndex, findNextUDA!(UDA, Sym, 0));
219 }
220 
221 unittest {
222 	import std.typetuple : TypeTuple;
223 
224 	struct Attribute {
225 		int x;
226 	}
227 
228 	enum Dummy;
229 
230 	@(Dummy, Attribute(21), Dummy, Attribute(42), Attribute(84)) void symbol() {
231 	}
232 
233 	@(Dummy, Attribute(21), Dummy, Attribute(42), Attribute) void wrong() {
234 	}
235 
236 	alias Cmp = TypeTuple!(Attribute(21), Attribute(42), Attribute(84));
237 	static assert(Cmp == UDATuple!(Attribute, symbol));
238 	static assert(!is(UDATuple!(Attribute, wrong)));
239 }