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