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