1 
2 module dquery.query;
3 
4 import std.traits;
5 import std.typetuple;
6 
7 import dquery.attribute;
8 import dquery.attributes;
9 import dquery.element;
10 import dquery.helper;
11 
12 struct DQuery(QueryType, QueryElements...)
13 {
14 
15 	alias elements this;
16 
17 	/++
18 	 + Returns the type being queried.
19 	 ++/
20 	@property
21 	alias type = QueryType;
22 
23 	/++
24 	 + Returns the elements in the query.
25 	 ++/
26 	@property
27 	alias elements = QueryElements;
28 
29 	/++
30 	 + Returns true if the query has no elements.
31 	 ++/
32 	@property
33 	alias empty = Alias!(length == 0);
34 
35 	/++
36 	 + Returns the number of elements in the query.
37 	 ++/
38 	@property
39 	alias length = Alias!(elements.length);
40 
41 	/++
42 	 + Returns the query with all duplicate elements removed.
43 	 ++/
44 	@property
45 	alias unique = Alias!(
46 		DQuery!(QueryType, NoDuplicates!QueryElements)()
47 	);
48 
49 	/++
50 	 + Return an uninitialized value of the query's type.
51 	 ++/
52 	@property
53 	static auto opCall()
54 	{
55 		DQuery!(QueryType, QueryElements) query = void;
56 		return query;
57 	}
58 
59 	/++
60 	 + Returns the query with all filters removed.
61 	 ++/
62 	@property
63 	static auto reset()()
64 	{
65 		import dquery.d;
66 		return query!QueryType;
67 	}
68 
69 	/++
70 	 + Returns the first element in the query.
71 	 ++/
72 	@property
73 	static auto first()()
74 	if(!empty)
75 	{
76 		return QueryElements[0];
77 	}
78 
79 	@property
80 	static auto firstOr(alias Fallback)()
81 	if(!is(typeof(Fallback) == void))
82 	{
83 		static if(!empty)
84 		{
85 			return QueryElements[0];
86 		}
87 		else
88 		{
89 			return Fallback;
90 		}
91 	}
92 
93 	/++
94 	 + Returns the type's attributes.
95 	 ++/
96 	@property
97 	static auto attributes()()
98 	{
99 		alias MapToAttribute(alias Attribute) = Alias!(
100 			DQueryAttribute!Attribute()
101 		);
102 
103 		return DQueryAttributes!(
104 			QueryType,
105 			staticMap!(
106 				MapToAttribute,
107 				GetAttributes!QueryType
108 			)
109 		)();
110 	}
111  
112 	/++
113 	 + Returns the type's allowed attributes.
114 	 ++/
115 	@property
116 	static auto attributes(Allow...)()
117 	if(Allow.length > 0)
118 	{
119 		return attributes.anyOf!Allow;
120 	}
121 
122 	/++
123 	 + Returns true if the type has all of the given attributes.
124 	 ++/
125 	@property
126 	static auto hasAllOf(TList...)()
127 	if(TList.length > 0)
128 	{
129 		return attributes.hasAllOf!TList;
130 	}
131 
132 	/++
133 	 + Returns true if the type has any of the given attributes.
134 	 ++/
135 	@property
136 	static auto hasAnyOf(TList...)()
137 	if(TList.length > 0)
138 	{
139 		return attributes.hasAnyOf!TList;
140 	}
141 
142 	/++
143 	 + Returns true if the type has none of the given attributes.
144 	 ++/
145 	@property
146 	static auto hasNoneOf(TList...)()
147 	if(TList.length > 0)
148 	{
149 		return attributes.hasNoneOf!TList;
150 	}
151 
152 	/++
153 	 + Filters elements that match the given name.
154 	 ++/
155 	@property
156 	static auto name(string Name)()
157 	{
158 		return names!Name;
159 	}
160 
161 	/++
162 	 + Filters elements that match one of the given names.
163 	 ++/
164 	@property
165 	static auto names(Names...)()
166 	{
167 		template NameFilter(alias Name)
168 		{
169 			alias NameFilter(alias Element) = Alias!(
170 				Element.isName!Name
171 			);
172 		}
173 
174 		auto query = DQuery!(QueryType, QueryElements)();
175 
176 		static if(query.length > 0)
177 		{
178 			return query.filter!(templateOr!(staticMap!(NameFilter, Names)));
179 		}
180 		else
181 		{
182 			return query;
183 		}
184 	}
185 
186 	/++
187 	 + Filters elements that match one of the given types.
188 	 ++/
189 	@property
190 	static auto types(Types...)()
191 	{
192 		template TypeFilter(Type)
193 		{
194 			alias TypeFilter(alias Element) = Alias!(
195 				Element.isTypeOf!Type
196 			);
197 		}
198 
199 		auto query = DQuery!(QueryType, QueryElements)();
200 
201 		static if(query.length > 0)
202 		{
203 			return query.filter!(templateOr!(staticMap!(TypeFilter, Types)));
204 		}
205 		else
206 		{
207 			return query;
208 		}
209 	}
210 
211 	/++
212 	 + Filters elements that return one of the given types.
213 	 ++/
214 	@property
215 	static auto returns(Types...)()
216 	{
217 		template ReturnFilter(Type)
218 		{
219 			alias ReturnFilter(alias Element) = Alias!(
220 				Element.isReturnTypeOf!Type
221 			);
222 		}
223 
224 		auto query = DQuery!(QueryType, QueryElements)();
225 
226 		static if(query.length > 0)
227 		{
228 			return query.filter!(templateOr!(staticMap!(ReturnFilter, Types)));
229 		}
230 		else
231 		{
232 			return qeury;
233 		}
234 	}
235 
236 	/++
237 	 + Filters elements that match the given arity value.
238 	 ++/
239 	@property
240 	static auto arity(int Arity)()
241 	{
242 		return arities!Arity;
243 	}
244 
245 	/++
246 	 + Filters elements that match one of the given arity values.
247 	 ++/
248 	@property
249 	static auto arities(Arities...)()
250 	{
251 		template ArityFilter(int Arity)
252 		{
253 			alias ArityFilter(alias Element) = Alias!(
254 				Element.isArity!Arity
255 			);
256 		}
257 
258 		auto query = DQuery!(QueryType, QueryElements)();
259 
260 		static if(query.length > 0)
261 		{
262 			return query.filter!(templateOr!(staticMap!(ArityFilter, Arities)));
263 		}
264 		else
265 		{
266 			return query;
267 		}
268 	}
269 
270 	/++
271 	 + Filters elements that match the given parameter list.
272 	 ++/
273 	@property
274 	static auto parameters(Parameters...)()
275 	{
276 		auto query = DQuery!(QueryType, QueryElements)();
277 
278 		static if(query.length > 0)
279 		{
280 			return query.filter!(f => f.isParameterTypesOf!Parameters);
281 		}
282 		else
283 		{
284 			return query;
285 		}
286 	}
287 
288 	/++
289 	 + Filters elements that have all of the given attributes.
290 	 ++/
291 	@property
292 	static auto allOf(Attributes...)()
293 	{
294 		template AllOfFilter(alias Attribute)
295 		{
296 			alias AllOfFilter(alias Element) = Alias!(
297 				Element.hasAttribute!Attribute
298 			);
299 		}
300 
301 		auto query = DQuery!(QueryType, QueryElements)();
302 
303 		static if(query.length > 0)
304 		{
305 			return query.filter!(templateAnd!(staticMap!(AllOfFilter, Attributes)));
306 		}
307 		else
308 		{
309 			return query;
310 		}
311 	}
312 
313 	/++
314 	 + Filters elements that have any of the given attributes.
315 	 ++/
316 	@property
317 	static auto anyOf(Attributes...)()
318 	{
319 		template AnyOfFilter(alias Attribute)
320 		{
321 			alias AnyOfFilter(alias Element) = Alias!(
322 				Element.hasAttribute!Attribute
323 			);
324 		}
325 
326 		auto query = DQuery!(QueryType, QueryElements)();
327 
328 		static if(query.length > 0)
329 		{
330 			return query.filter!(templateOr!(staticMap!(AnyOfFilter, Attributes)));
331 		}
332 		else
333 		{
334 			return query;
335 		}
336 	}
337 
338 	/++
339 	 + Filters elements that have none of the given attributes.
340 	 ++/
341 	@property
342 	static auto noneOf(Attributes...)()
343 	{
344 		template NoneOfFilter(alias Attribute)
345 		{
346 			alias NoneOfFilter(alias Element) = Alias!(
347 				!Element.hasAttribute!Attribute
348 			);
349 		}
350 
351 		auto query = DQuery!(QueryType, QueryElements)();
352 
353 		static if(query.length > 0)
354 		{
355 			return query.filter!(templateAnd!(staticMap!(NoneOfFilter, Attributes)));
356 		}
357 		else
358 		{
359 			return query;
360 		}
361 	}
362 
363 	/++
364 	 + Provides validations regarding the query's length.
365 	 ++/
366 	@property
367 	template ensure(string Attr : "length")
368 	{
369 		import std.conv : text;
370 
371 		@property
372 		static auto minimum(size_t Min,
373 			string Message = "Length cannot be less than " ~ Min.text)()
374 		{
375 			static assert(length >= Min, Message);
376 			return DQuery!(QueryType, QueryElements)();
377 		}
378 
379 		@property
380 		static auto maximum(size_t Max,
381 			string Message = "Length cannot be greater than " ~ Max.text)()
382 		{
383 			static assert(length <= Max, Message);
384 			return DQuery!(QueryType, QueryElements)();
385 		}
386 
387 		@property
388 		static auto between(size_t Min, size_t Max,
389 			string Message = "Length must be between " ~ Min.text ~ " and " ~ Max.text)()
390 		{
391 			static assert(length >= Min && length <= Max, Message);
392 			return DQuery!(QueryType, QueryElements)();
393 		}
394 
395 		@property
396 		static auto exactly(size_t Length,
397 			string Message = "Length must be exactly " ~ Length.text)()
398 		{
399 			static assert(length == Length, Message);
400 			return DQuery!(QueryType, QueryElements)();
401 		}
402 	}
403 
404 	/++
405 	 + Filters elements that are fields.
406 	 ++/
407 	@property
408 	static auto fields()()
409 	{
410 		auto query = DQuery!(QueryType, QueryElements)();
411 
412 		static if(query.length > 0)
413 		{
414 			return query.filter!(f => f.isField);
415 		}
416 		else
417 		{
418 			return query;
419 		}
420 	}
421 
422 	/++
423 	 + Filters elements that are fields and match any of the given names.
424 	 ++/
425 	@property
426 	static auto fields(Names...)()
427 	if(Names.length > 0)
428 	{
429 		return fields.names!Names;
430 	}
431 
432 	/++
433 	 + Filters elements that are functions.
434 	 ++/
435 	@property
436 	static auto functions()()
437 	{
438 		auto query = DQuery!(QueryType, QueryElements)();
439 
440 		static if(query.length > 0)
441 		{
442 			return query.filter!(f => f.isFunction);
443 		}
444 		else
445 		{
446 			return query;
447 		}
448 	}
449 
450 	/++
451 	 + Filters elements that are functions and match any of the given names
452 	 ++/
453 	@property
454 	static auto functions(Names...)()
455 	if(Names.length > 0)
456 	{
457 		return functions.names!Names;
458 	}
459 
460 	/++
461 	 + Filters elements that are constructors.
462 	 ++/
463 	@property
464 	static auto constructors()()
465 	{
466 		auto query = DQuery!(QueryType, QueryElements)();
467 
468 		static if(query.length > 0)
469 		{
470 			return query.filter!(f => f.isConstructor);
471 		}
472 		else
473 		{
474 			return query;
475 		}
476 	}
477 
478 	/++
479 	 + Filters elements that are destructors.
480 	 ++/
481 	@property
482 	static auto destructors()()
483 	{
484 		auto query = DQuery!(QueryType, QueryElements)();
485 
486 		static if(query.length > 0)
487 		{
488 			return query.filter!(f => f.isDestructor);
489 		}
490 		else
491 		{
492 			return query;
493 		}
494 	}
495 
496 	/++
497 	 + Filters elements that are aggregate types.
498 	 ++/
499 	@property
500 	static auto aggregates()()
501 	{
502 		auto query = DQuery!(QueryType, QueryElements)();
503 
504 		static if(query.length > 0)
505 		{
506 			return query.filter!(f => f.isAggregate);
507 		}
508 		else
509 		{
510 			return query;
511 		}
512 	}
513 
514 	/++
515 	 + Filters elements that are aggregate types and match any of the given names.
516 	 ++/
517 	@property
518 	static auto aggregates(Names...)()
519 	if(Names.length > 0)
520 	{
521 		return aggregates.names!Names;
522 	}
523 
524 	/++
525 	 + Returns a union between this query and another one.
526 	 +
527 	 + Params:
528 	 +     query = The other query being joined with this one.
529 	 ++/
530 	@property
531 	static auto join(OType, OElements...)(DQuery!(OType, OElements) query)
532 	{
533 		return DQuery!(QueryType, TypeTuple!(QueryElements, OElements))();
534 	}
535 
536 }
537 
538 /++
539  + Tests if all elements in a query satisfy a predicate template or function.
540  ++/
541 template all(alias Pred)
542 {
543 	/++
544 	 + An empty query always produces true.
545 	 ++/
546 	@property
547 	bool all(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
548 	if(QueryElements.length == 0)
549 	{
550 		return true;
551 	}
552 
553 	@property
554 	bool all(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
555 	if(QueryElements.length > 0 && __traits(compiles, {
556 		foreach(Element; QueryElements)
557 		{
558 			if(!Pred(Element))
559 			{
560 				return false;
561 			}
562 		}
563 
564 		return true;
565 	}))
566 	{
567 		foreach(Element; QueryElements)
568 		{
569 			if(!Pred(Element))
570 			{
571 				return false;
572 			}
573 		}
574 
575 		return true;
576 	}
577 
578 	@property
579 	bool all(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
580 	if(QueryElements.length > 0 && __traits(compiles, {
581 		foreach(Element; QueryElements)
582 		{
583 			static if(!Pred!(Element))
584 			{
585 				return false;
586 			}
587 		}
588 
589 		return true;
590 	}))
591 	{
592 		foreach(Element; QueryElements)
593 		{
594 			static if(!Pred!(Element))
595 			{
596 				return false;
597 			}
598 		}
599 
600 		return true;
601 	}
602 }
603 
604 /++
605  + Tests if any elements in a query statisfy a template predicate or function.
606  ++/
607 template any(alias Pred)
608 {
609 	/++
610 	 + An empty query always produces true.
611 	 ++/
612 	@property
613 	bool any(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
614 	if(QueryElements.length == 0)
615 	{
616 		return true;
617 	}
618 
619 	@property
620 	bool any(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
621 	if(QueryElements.length > 0 && __traits(compiles, {
622 		foreach(Element; QueryElements)
623 		{
624 			if(Pred(Element))
625 			{
626 				return true;
627 			}
628 		}
629 
630 		return false;
631 	}))
632 	{
633 		foreach(Element; QueryElements)
634 		{
635 			if(Pred(Element))
636 			{
637 				return true;
638 			}
639 		}
640 
641 		return false;
642 	}
643 
644 	@property
645 	bool any(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
646 	if(QueryElements.length > 0 && __traits(compiles, {
647 		foreach(Element; QueryElements)
648 		{
649 			if(Pred!(Element))
650 			{
651 				return true;
652 			}
653 		}
654 
655 		return false;
656 	}))
657 	{
658 		foreach(Element; QueryElements)
659 		{
660 			if(Pred!(Element))
661 			{
662 				return true;
663 			}
664 		}
665 
666 		return false;
667 	}
668 }
669 
670 /++
671  + Iterates over elements in a query using a unary template or function.
672  ++/
673 template each(alias Pred)
674 {
675 	@property
676 	auto each(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
677 	if(QueryElements.length == 0)
678 	{
679 		return query;
680 	}
681 
682 	@property
683 	auto each(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
684 	if(QueryElements.length > 0 && __traits(compiles, {
685 		foreach(element; QueryElements)
686 		{
687 			Pred(element);
688 		}
689 	}))
690 	{
691 		foreach(element; QueryElements)
692 		{
693 			Pred(element);
694 		}
695 		
696 		return query;
697 	}
698 
699 	@property
700 	auto each(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
701 	if(QueryElements.length > 0 && __traits(compiles, {
702 		foreach(Element; QueryElements)
703 		{
704 			Pred!Element;
705 		}
706 	}))
707 	{
708 		foreach(Element; QueryElements)
709 		{
710 			Pred!Element;
711 		}
712 		
713 		return query;
714 	}
715 }
716 
717 /++
718  + Applies a map transformation to a query using a unary template or function.
719  ++/
720 template map(alias Pred)
721 {
722 	@property
723 	auto map(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
724 	if(QueryElements.length == 0)
725 	{
726 		return [];
727 	}
728 
729 	@property
730 	auto map(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
731 	if(QueryElements.length > 0 && __traits(compiles, {
732 		return [staticMap!(UnaryToPred!Pred, QueryElements)];
733 	}))
734 	{
735 		return [staticMap!(UnaryToPred!Pred, QueryElements)];
736 	}
737 
738 	@property
739 	auto map(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
740 	if(QueryElements.length > 0 && __traits(compiles, {
741 		return [staticMap!(Pred, QueryElements)];
742 	}))
743 	{
744 		return [staticMap!(Pred, QueryElements)];
745 	}
746 }
747 
748 /++
749  + Applies a filter transformation to a query using a unary template or function.
750  ++/
751 template filter(alias Pred)
752 {
753 	@property
754 	auto filter(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
755 	if(QueryElements.length == 0)
756 	{
757 		return query;
758 	}
759 
760 	@property
761 	auto filter(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
762 	if(QueryElements.length > 0 && __traits(compiles, {
763 		return DQuery!(QueryType, Filter!(UnaryToPred!Pred, QueryElements))();
764 	}))
765 	{
766 		return DQuery!(QueryType, Filter!(UnaryToPred!Pred, QueryElements))();
767 	}
768 
769 	@property
770 	auto filter(QueryType, QueryElements...)(DQuery!(QueryType, QueryElements) query)
771 	if(QueryElements.length > 0 && __traits(compiles, {
772 		return DQuery!(QueryType, Filter!(Pred, QueryElements))();
773 	}))
774 	{
775 		return DQuery!(QueryType, Filter!(Pred, QueryElements))();
776 	}
777 }