1 
2 module dquery.attributes;
3 
4 import std.typetuple;
5 
6 import dquery.attribute;
7 import dquery.helper;
8 
9 struct DQueryAttributes(QueryType, Attributes...)
10 {
11 
12 	alias attributes this;
13 
14 	/++
15 	 + Returns the type being queried.
16 	 ++/
17 	@property
18 	alias type = QueryType;
19 
20 	/++
21 	 + Returns a tuple of attributes in the query.
22 	 ++/
23 	@property
24 	alias attributes = Attributes;
25 
26 	/++
27 	 + Returns true if the list of attributes is empty.
28 	 ++/
29 	@property
30 	enum empty = length == 0;
31 
32 	/++
33 	 + Returns the number of attributes in the query.
34 	 ++/
35 	@property
36 	enum length = Attributes.length;
37 
38 	/++
39 	 + Returns a transformed list of attributes with all duplicateds removed.
40 	 ++/
41 	@property
42 	enum unique = DQueryAttributes!(QueryType, NoDuplicates!Attributes)();
43 
44 	/++
45 	 + Returns an uninitialized value of the query's type.
46 	 ++/
47 	@property
48 	static auto opCall()
49 	{
50 		DQueryAttributes!(QueryType, Attributes) attributes = void;
51 		return attributes;
52 	}
53 
54 	/++
55 	 + Returns a query for the type that produced this attribute query.
56 	 ++/
57 	@property
58 	static auto parent()()
59 	{
60 		import dquery.d;
61 		return query!QueryType;
62 	}
63 
64 	/++
65 	 + Returns the first attribute in the query.
66 	 ++/
67 	@property
68 	static auto first()()
69 	{
70 		static assert(!empty, "Attributes query is empty.");
71 		return Attributes[0];
72 	}
73 
74 	/++
75 	 + Returns the first attribute in the query, or a fallback if empty.
76 	 ++/
77 	@property
78 	static auto firstOr(alias Fallback)()
79 	if(!is(typeof(Fallback) == void))
80 	{
81 		// Deprecate in a future version.
82 		return firstOrElse!Fallback;
83 	}
84 
85 	/++
86 	 + Returns the first attribute in the query, or a fallback if empty.
87 	 ++/
88 	@property
89 	static auto firstOrElse(alias Fallback)()
90 	if(!is(typeof(Fallback) == void))
91 	{
92 		static if(!empty)
93 		{
94 			return Attributes[0];
95 		}
96 		else
97 		{
98 			return DQueryAttribute!Fallback();
99 		}
100 	}
101 
102 	/++
103 	 + Returns the first attribute in the query, or throw if empty.
104 	 ++/
105 	@property
106 	static auto firstOrThrow()(lazy Throwable t)
107 	{
108 		static if(!empty)
109 		{
110 			return Attributes[0];
111 		}
112 		else
113 		{
114 			throw t;
115 		}
116 	}
117 
118 	/++
119 	 + Returns true if all of the given attributes are present.
120 	 ++/
121 	@property
122 	static auto hasAllOf(TList...)()
123 	if(TList.length > 0)
124 	{
125 		auto query = DQueryAttributes!(QueryType, Attributes)();
126 
127 		static if(TList.length > 1)
128 		{
129 			return query.hasAnyOf!(TList[0]) && query.hasAllOf!(TList[1 .. $]);
130 		}
131 		else
132 		{
133 			return query.hasAnyOf!(TList[0]);
134 		}
135 	}
136 
137 	/++
138 	 + Returns true if any of the given attributes are present.
139 	 ++/
140 	@property
141 	static auto hasAnyOf(TList...)()
142 	if(TList.length > 0)
143 	{
144 		auto query = DQueryAttributes!(QueryType, Attributes)();
145 		return !query.anyOf!TList.empty;
146 	}
147 
148 	/++
149 	 + Returns true if none of the given attributes are present.
150 	 ++/
151 	@property
152 	static auto hasNoneOf(TList...)()
153 	if(TList.length > 0)
154 	{
155 		auto query = DQueryAttributes!(QueryType, Attributes)();
156 		return query.anyOf!TList.empty;
157 	}
158 
159 	/++
160 	 + Returns a subset of the list of attributes which match
161 	 + at least one of the given types.
162 	 ++/
163 	@property
164 	static auto anyOf(TList...)()
165 	if(TList.length > 0)
166 	{
167 		alias AnyOfFilter(Type) = Alias!(
168 			UnaryToPred!(attribute => attribute.isTypeOf!Type)
169 		);
170 
171 		auto query = DQueryAttributes!(QueryType, Attributes)();
172 
173 		static if(Attributes.length > 0)
174 		{
175 			alias Pred = templateOr!(staticMap!(AnyOfFilter, TList));
176 			return query.filter!Pred;
177 		}
178 		else
179 		{
180 			return query;
181 		}
182 	}
183 
184 	/++
185 	 + Property that returns a subset of the list of attributes
186 	 + which match none of the given types.
187 	 ++/
188 	@property
189 	static auto noneOf(TList...)()
190 	if(TList.length > 0)
191 	{
192 		alias NoneOfFilter(Type) = Alias!(
193 			UnaryToPred!(attribute => !attribute.isTypeOf!Type)
194 		);
195 
196 		auto query = DQueryAttributes!(QueryType, Attributes)();
197 
198 		static if(Attributes.length > 0)
199 		{
200 			alias Pred = templateAnd!(staticMap!(NoneOfFilter, TList));
201 			return query.filter!Pred;
202 		}
203 		else
204 		{
205 			return query;
206 		}
207 	}
208 
209 	/++
210 	 + Provides query validation functions tied to length.
211 	 ++/
212 	@property
213 	template ensure(string Attr : "length")
214 	{
215 		import std.conv : text;
216 
217 		@property
218 		static auto minimum(size_t Min,
219 			string Message = "Length cannot be less than " ~ Min.text)()
220 		{
221 			static assert(length >= Min, Message);
222 			return DQueryAttributes!(QueryType, Attributes)();
223 		}
224 
225 		@property
226 		static auto maximum(size_t Max,
227 			string Message = "Length cannot be greater than " ~ Max.text)()
228 		{
229 			static assert(length <= Max, Message);
230 			return DQueryAttributes!(QueryType, Attributes)();
231 		}
232 
233 		@property
234 		static auto between(size_t Min, size_t Max,
235 			string Message = "Length must be between " ~ Min.text ~ " and " ~ Max.text)()
236 		{
237 			static assert(length >= Min && length <= Max, Message);
238 			return DQueryAttributes!(QueryType, Attributes)();
239 		}
240 
241 		@property
242 		static auto exactly(size_t Length,
243 			string Message = "Length must be exactly " ~ Length.text)()
244 		{
245 			static assert(length == Length, Message);
246 			return DQueryAttributes!(QueryType, Attributes)();
247 		}
248 	}
249 
250 }
251 
252 template filter(alias Pred)
253 {
254 	@property
255 	auto filter(QueryType, Attributes...)(DQueryAttributes!(QueryType, Attributes) attributes)
256 	if(Attributes.length == 0)
257 	{
258 		return attributes;
259 	}
260 
261 	@property
262 	auto filter(QueryType, Attributes...)(DQueryAttributes!(QueryType, Attributes) attributes)
263 	if(Attributes.length > 0 && __traits(compiles, {
264 		DQueryAttributes!(QueryType, Filter!(UnaryToPred!Pred, Attributes)) result = void;
265 	}))
266 	{
267 		DQueryAttributes!(QueryType, Filter!(UnaryToPred!Pred, Attributes)) result = void;
268 		return result;
269 	}
270 
271 	@property
272 	auto filter(QueryType, Attributes...)(DQueryAttributes!(QueryType, Attributes) attributes)
273 	if(Attributes.length > 0 && __traits(compiles, {
274 		DQueryAttributes!(QueryType, Filter!(Pred, Attributes)) result = void;
275 	}))
276 	{
277 		DQueryAttributes!(QueryType, Filter!(Pred, Attributes)) result = void;
278 		return result;
279 	}
280 }