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 }