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 }