1 // Written in D programming language 2 /** 3 * Module handles functions and templates that we lazied to factor out to separate module. 4 * 5 * Function categories: 6 * <ul> 7 * <li> JSON de/serialization based on annotations </li> 8 * <li> Exception handling functions </li> 9 * <li> Cheat casting functions </li> 10 * <li> String processing functions (the only one $(B fromStringz))</li> 11 * <li> Introspecting templates that cannot be found in Phobos </li> 12 * <li> Functional styled utilities for optional values and lazy ranges</li> 13 * </ul> 14 * 15 * Copyright: © 2014 DSoftOut 16 * License: Subject to the terms of the MIT license, as written in the included LICENSE file. 17 * Authors: Zaramzan <shamyan.roman@gmail.com>, 18 * NCrashed <ncrashed@gmail.com> 19 */ 20 module util; 21 22 import std.algorithm; 23 import std.exception; 24 import std.string; 25 import std.conv; 26 import std.container; 27 import std.traits; 28 import std.typecons; 29 import std.typetuple; 30 import std.range; 31 import std.path; 32 import vibe.data.json; 33 34 enum APPNAME = "pgator"; 35 36 mixin template t_field(T, alias fieldName) 37 { 38 mixin("private "~T.stringof~" m_"~fieldName~";"); 39 40 mixin("private bool f_"~fieldName~";"); 41 42 mixin(T.stringof~" "~fieldName~"() @property { return m_"~fieldName~";}"); 43 44 mixin("private void "~fieldName~"("~T.stringof~" f) @property { m_"~fieldName~"= f; f_"~fieldName~"=true;}"); 45 } 46 47 //For deserializeFromJson 48 enum required; 49 50 //For deserializeFromJson 51 enum possible; 52 53 54 /** 55 * Deserializes from Json to type T<br> 56 * 57 * Supported only structs yet 58 * 59 * Example: 60 * ---- 61 * struct S 62 * { 63 * @required 64 * int a; //get value from Json. Throws RequiredFieldException 65 * 66 * @possible 67 * int b; //tries to get value from Json 68 * 69 * int c; //will be ignored 70 * } 71 * 72 * auto s = deserializeFromJson!S(json); 73 * ------ 74 * 75 * Authors: Zaramzan <shamyan.roman@gmail.com> 76 */ 77 T deserializeFromJson(T)(Json src) if(is(T == struct)) 78 { 79 T ret; 80 81 if (src.type != Json.Type.object) 82 { 83 throw new RequiredJsonObject("Required json object"); 84 } 85 86 void processStruct(string mem, bool isMemberOptional, Ret)(out Ret retMem, Json srcMem) 87 { 88 static if (is(Ret == Json)) 89 { 90 retMem = srcMem; 91 } 92 else 93 { 94 if (srcMem.type == Json.Type.object) 95 { 96 retMem = deserializeFromJson!Ret(srcMem); 97 } 98 else 99 { 100 // issue #96, is struct has default constructor 101 // and marked with @possible and received null 102 static if(isMemberOptional && __traits(compiles, Ret() )) 103 { 104 if(srcMem.type == Json.Type.null_) 105 { 106 retMem = Ret(); 107 } else 108 { 109 throw new RequiredFieldException(text("Field ", mem, " must be object in json: ", src)); 110 } 111 } else 112 { 113 throw new RequiredFieldException(text("Field ", mem, " must be object in json: ", src)); 114 } 115 } 116 } 117 } 118 119 void processArray(string mem, bool isMemberOptional, Ret)(out Ret retMem, Json srcMem) 120 { 121 if (srcMem.type == Json.Type.array) 122 { 123 alias ElementType!Ret ElemType; 124 125 ElemType[] arr = new ElemType[0]; 126 127 foreach(json; srcMem) 128 { 129 static if (is(ElemType == struct)) 130 { 131 arr ~= deserializeFromJson!ElemType(json); 132 } 133 else 134 { 135 static if(__traits(compiles, {ElemType t = null;})) 136 { 137 if(json.type == Json.Type.null_) 138 { 139 arr ~= null; 140 continue; 141 } 142 } 143 arr ~= json.to!ElemType; 144 } 145 } 146 147 retMem = arr; 148 } 149 else 150 { 151 // array could be a null for @possible. issue #96 152 static if(isMemberOptional) 153 { 154 if(srcMem.type == Json.Type.null_) 155 { 156 retMem = null; 157 } else 158 { 159 throw new RequiredFieldException(text("Field ", mem, " must be array in json: ", src)); 160 } 161 } else 162 { 163 throw new RequiredFieldException(text("Field ", mem, " must be array in json: ", src)); 164 } 165 } 166 } 167 168 foreach(mem; __traits(allMembers, T)) 169 { 170 alias getMemberType!(T, mem) MemberType; 171 enum isMemberRequired = isRequired!(mem, T); 172 enum isMemberOptional = isOptional!(mem, T); 173 174 enum srcMem = "src."~mem; 175 enum retMem = "ret."~mem; 176 177 static if (isMemberRequired || isMemberOptional) 178 { 179 if (mixin(srcMem).type != Json.Type.undefined) 180 { 181 static if(is(MemberType == struct)) 182 { 183 processStruct!(mem, isMemberOptional)(mixin(retMem), mixin(srcMem)); 184 } 185 else static if(isArray!MemberType && !isSomeString!MemberType) 186 { 187 processArray!(mem, isMemberOptional)(mixin(retMem), mixin(srcMem)); 188 } 189 else 190 { 191 mixin(retMem) = mixin(srcMem).to!MemberType(); 192 } 193 } 194 else static if (isMemberRequired) 195 { 196 throw new RequiredFieldException(text("Field ", mem, " required in json: ", src)); 197 } 198 } 199 200 } 201 202 return ret; 203 } 204 unittest // issue #96 205 { 206 struct A 207 { 208 @possible 209 ubyte[] a; 210 } 211 212 auto a = A(null); 213 assert(deserializeFromJson!A(serializeToJson(a)) == a); 214 215 struct B 216 { 217 @possible 218 A a; 219 } 220 221 auto b = B(A(null)); 222 assert(deserializeFromJson!B(serializeToJson(b)) == b); 223 } 224 225 /** 226 * Serializes struct with $(B @required) attributes fields to Json <br> 227 * 228 * Example 229 * ------ 230 * struct S 231 * { 232 * @required 233 * int a = 1; //will be used 234 * 235 * @possible 236 * int b = 2; //will be ignored 237 * 238 * int c; //will be ignored 239 * } 240 * 241 * writeln(serializeRequiredToJson(S())); // { "a":1 } 242 * ------ 243 */ 244 Json serializeRequiredToJson(T)(T val) 245 { 246 static assert (is(T == struct), "Need struct type, not "~T.stringof); 247 248 Json ret = Json.emptyObject; 249 250 foreach(mem; __traits(allMembers, T)) 251 { 252 static if (isRequired!(mem, T)) 253 { 254 alias getMemberType!(T, mem) MemType; 255 256 alias vibe.data.json.serializeToJson vibeSer; 257 258 static if (is(MemType == struct)) 259 { 260 ret[mem] = serializeRequiredToJson!MemType(mixin("val."~mem)); 261 } 262 else static if (isArray!MemType) 263 { 264 alias ElementType!MemType EType; 265 static if (is(EType == struct)) 266 { 267 auto j1 = Json.emptyArray; 268 269 foreach(elem; mixin("val."~mem)) 270 { 271 j1 ~= serializeRequiredToJson(elem); 272 } 273 274 ret[mem] = j1; 275 } 276 else 277 { 278 ret[mem] = vibeSer(mixin("val."~mem)); 279 } 280 } 281 else 282 { 283 ret[mem] = vibeSer(mixin("val."~mem)); 284 } 285 } 286 } 287 288 return ret; 289 } 290 291 private bool isRequired(alias mem, T)() 292 { 293 bool flag = false; 294 foreach(attr;__traits(getAttributes, mixin("T."~mem))) 295 { 296 static if (is(attr == required)) 297 { 298 flag = true; 299 break; 300 } 301 } 302 303 return flag; 304 } 305 306 private bool isOptional(alias mem, T)() 307 { 308 bool flag = false; 309 foreach(attr;__traits(getAttributes, mixin("T."~mem))) 310 { 311 static if (is(attr == possible)) 312 { 313 flag = true; 314 break; 315 } 316 } 317 318 return flag; 319 } 320 321 class RequiredFieldException:Exception 322 { 323 this(in string msg) 324 { 325 super(msg); 326 } 327 } 328 329 class RequiredJsonObject:Exception 330 { 331 this(in string msg) 332 { 333 super(msg); 334 } 335 } 336 337 /** 338 * Tries to call function. On exception throws Ex, otherwise return func() result 339 * 340 * Authors: Zaramzan <shamyan.roman@gmail.com> 341 */ 342 template tryEx(Ex, alias func) 343 { 344 static assert(isSomeFunction!func, "func must be some function"); 345 static assert(is(Ex:Exception), "Ex must be Exception"); 346 347 auto wrapper(TP...)(TP params) 348 { 349 try 350 { 351 return func(params); 352 } 353 catch(Exception ex) 354 { 355 throw new Ex(ex.msg, ex.file, ex.line); 356 } 357 } 358 359 alias wrapper tryEx; 360 } 361 362 /** 363 * Tries to evaluate par. On exception throws Ex, otherwise return par 364 * 365 * Authors: Zaramzan <shamyan.roman@gmail.com> 366 */ 367 T tryEx(Ex, T)(lazy T par) 368 { 369 static assert(is(Ex:Exception), "Ex must be Exception"); 370 371 try 372 { 373 return par; 374 } 375 catch(Exception ex) 376 { 377 throw new Ex(ex.msg, ex.file, ex.line); 378 } 379 } 380 381 /** 382 * cast to shared type T 383 * 384 * Warning: 385 * Don't use this, if you want send object to another thread. It just dirty hack. 386 */ 387 template toShared(T) 388 { 389 private alias Unqual!T P; 390 391 shared(P) toShared(T par) 392 { 393 return cast(shared P) cast(P) par; 394 } 395 } 396 397 /// cast to unqual type T 398 template toUnqual(T) 399 { 400 private alias Unqual!T P; 401 402 P toUnqual(T par) 403 { 404 return cast(P) par; 405 } 406 } 407 408 version(unittest) 409 { 410 bool thrower() 411 { 412 throw new Exception("Exception"); 413 } 414 415 class TestException: Exception 416 { 417 @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) 418 { 419 super(msg, file, line, next); 420 } 421 } 422 } 423 424 unittest 425 { 426 import std.exception; 427 428 assertThrown!TestException(tryEx!TestException(thrower())); 429 } 430 431 static if (__VERSION__ < 2066) { // from phobos 2.066-b1 432 import std.c.string; 433 434 /++ 435 Returns a D-style array of $(D char) given a zero-terminated C-style string. 436 The returned array will retain the same type qualifiers as the input. 437 438 $(RED Important Note:) The returned array is a slice of the original buffer. 439 The original data is not changed and not copied. 440 +/ 441 442 inout(char)[] fromStringz(inout(char)* cString) @system pure { 443 return cString ? cString[0 .. strlen(cString)] : null; 444 } 445 446 /// 447 @system pure unittest 448 { 449 assert(fromStringz(null) == null); 450 assert(fromStringz("foo") == "foo"); 451 } 452 } else { 453 public import std.string: fromStringz; 454 } 455 456 /// getMemberType 457 /** 458 * Retrieves member type with $(D name) of class $(D Class). If member is agregate 459 * type declaration or simply doesn't exist, retrieves no type. You can check it with 460 * $(D is) operator. 461 * 462 * Example: 463 * ----------- 464 * class A 465 * { 466 * int aField; 467 * string b; 468 * bool c; 469 * 470 * class B {} 471 * struct C {} 472 * union D {} 473 * interface E {} 474 * } 475 * 476 * static assert(is(getMemberType!(A, "aField") == int)); 477 * static assert(is(getMemberType!(A, "b") == string)); 478 * static assert(is(getMemberType!(A, "c") == bool)); 479 * 480 * static assert(!is(getMemberType!(A, "B"))); 481 * static assert(!is(getMemberType!(A, "C"))); 482 * static assert(!is(getMemberType!(A, "D"))); 483 * static assert(!is(getMemberType!(A, "E"))); 484 * ----------- 485 */ 486 template getMemberType(Class, string name) 487 { 488 static if(hasMember!(Class, name)) 489 alias typeof(__traits(getMember, Class, name)) getMemberType; 490 } 491 492 unittest 493 { 494 class A 495 { 496 int a; 497 string b; 498 bool c; 499 500 class B {} 501 struct C {} 502 union D {} 503 interface E {} 504 } 505 506 static assert(is(getMemberType!(A, "a") == int)); 507 static assert(is(getMemberType!(A, "b") == string)); 508 static assert(is(getMemberType!(A, "c") == bool)); 509 510 static assert(!is(getMemberType!(A, "B"))); 511 static assert(!is(getMemberType!(A, "C"))); 512 static assert(!is(getMemberType!(A, "D"))); 513 static assert(!is(getMemberType!(A, "E"))); 514 } 515 516 /** 517 * Struct-wrapper to handle result of computations, 518 * that can fail. 519 * 520 * Example: 521 * --------- 522 * class A {} 523 * 524 * auto a = new A(); 525 * auto ma = Maybe!A(a); 526 * auto mb = Maybe!A(null); 527 * 528 * assert(!ma.isNothing); 529 * assert(mb.isNothing); 530 * 531 * assert(ma.get == a); 532 * assertThrown!Error(mb.get); 533 * 534 * bool ncase = false, jcase = false; 535 * ma.map(() {ncase = true;}, (v) {jcase = true;}); 536 * assert(jcase && !ncase); 537 * 538 * ncase = jcase = false; 539 * mb.map(() {ncase = true;}, (v) {jcase = true;}); 540 * assert(!jcase && ncase); 541 * --------- 542 */ 543 struct Maybe(T) 544 if(is(T == class) || is(T == interface) 545 || isPointer!T || isArray!T) 546 { 547 private T value; 548 549 /// Alias to stored type 550 alias T StoredType; 551 552 /** 553 * Constructing Maybe from $(B value). 554 * If pointer is $(B null) methods: $(B isNothing) returns true and 555 * $(B get) throws Error. 556 */ 557 this(T value) pure 558 { 559 this.value = value; 560 } 561 562 /** 563 * Constructing empty Maybe. 564 * If Maybe is created with the method, it is considred empty 565 * and $(B isNothing) returns false. 566 */ 567 static Maybe!T nothing() 568 { 569 return Maybe!T(null); 570 } 571 572 /// Returns true if stored value is null 573 bool isNothing() const 574 { 575 return value is null; 576 } 577 578 /** 579 * Unwrap value from Maybe. 580 * If stored value is $(B null), Error is thrown. 581 */ 582 T get() 583 { 584 assert(value !is null, "Stored reference is null!"); 585 return value; 586 } 587 588 /** 589 * Unwrap value from Maybe. 590 * If stored value is $(B null), Error is thrown. 591 */ 592 const(T) get() const 593 { 594 assert(value !is null, "Stored reference is null!"); 595 return value; 596 } 597 598 /** 599 * If struct holds $(B null), then $(B nothingCase) result 600 * is returned. If struct holds not $(B null) value, then 601 * $(justCase) result is returned. $(B justCase) is fed 602 * with unwrapped value. 603 */ 604 U map(U)(U delegate() nothingCase, U delegate(T) justCase) 605 { 606 return isNothing ? nothingCase() : justCase(value); 607 } 608 609 /** 610 * If struct holds $(B null), then $(B nothingCase) result 611 * is returned. If struct holds not $(B null) value, then 612 * $(justCase) result is returned. $(B justCase) is fed 613 * with unwrapped value. 614 */ 615 U map(U)(U delegate() nothingCase, U delegate(const T) justCase) const 616 { 617 return isNothing ? nothingCase() : justCase(value); 618 } 619 } 620 621 unittest 622 { 623 class A {} 624 625 auto a = new A(); 626 auto ma = Maybe!A(a); 627 auto mb = Maybe!A(null); 628 629 assert(!ma.isNothing); 630 assert(mb.isNothing); 631 632 assert(ma.get == a); 633 assertThrown!Error(mb.get); 634 635 bool ncase = false, jcase = false; 636 ma.map(() {ncase = true;}, (v) {jcase = true;}); 637 assert(jcase && !ncase); 638 639 ncase = jcase = false; 640 mb.map(() {ncase = true;}, (v) {jcase = true;}); 641 assert(!jcase && ncase); 642 } 643 644 /** 645 * Struct-wrapper to handle result of computations, 646 * that can fail. 647 * 648 * Example: 649 * --------- 650 * struct A {} 651 * 652 * auto ma = Maybe!A(A()); 653 * auto mb = Maybe!A.nothing; 654 * 655 * assert(!ma.isNothing); 656 * assert(mb.isNothing); 657 * 658 * assert(ma.get == A()); 659 * assertThrown!Error(mb.get); 660 * 661 * bool ncase = false, jcase = false; 662 * ma.map(() {ncase = true;}, (v) {jcase = true;}); 663 * assert(jcase && !ncase); 664 * 665 * ncase = jcase = false; 666 * mb.map(() {ncase = true;}, (v) {jcase = true;}); 667 * assert(!jcase && ncase); 668 * --------- 669 */ 670 struct Maybe(T) 671 if(is(T == struct) || isAssociativeArray!T || isBasicType!T) 672 { 673 private bool empty; 674 private T value; 675 676 /// Alias to stored type 677 alias T StoredType; 678 679 /** 680 * Constructing empty Maybe. 681 * If Maybe is created with the method, it is considred empty 682 * and $(B isNothing) returns false. 683 */ 684 static Maybe!T nothing() 685 { 686 Maybe!T ret; 687 ret.empty = true; 688 return ret; 689 } 690 691 /** 692 * Constructing Maybe from $(B value). 693 * If Maybe is created with the constructor, it is considered non empty 694 * and $(B isNothing) returns false. 695 */ 696 this(T value) pure 697 { 698 this.value = value; 699 empty = false; 700 } 701 702 /// Returns true if stored value is null 703 bool isNothing() const 704 { 705 return empty; 706 } 707 708 /** 709 * Unwrap value from Maybe. 710 * If the Maybe is empty, Error is thrown. 711 */ 712 T get() 713 { 714 assert(!empty, "Stored value is null!"); 715 return value; 716 } 717 718 /** 719 * Unwrap value from Maybe. 720 * If the Maybe is empty, Error is thrown. 721 */ 722 const(T) get() const 723 { 724 assert(!empty, "Stored value is null!"); 725 return value; 726 } 727 728 /** 729 * If struct holds $(B null), then $(B nothingCase) result 730 * is returned. If struct holds not $(B null) value, then 731 * $(justCase) result is returned. $(B justCase) is fed 732 * with unwrapped value. 733 */ 734 U map(U)(U delegate() nothingCase, U delegate(T) justCase) 735 { 736 return isNothing ? nothingCase() : justCase(value); 737 } 738 739 /** 740 * If struct holds $(B null), then $(B nothingCase) result 741 * is returned. If struct holds not $(B null) value, then 742 * $(justCase) result is returned. $(B justCase) is fed 743 * with unwrapped value. 744 */ 745 U map(U)(U delegate() nothingCase, U delegate(const T) justCase) const 746 { 747 return isNothing ? nothingCase() : justCase(value); 748 } 749 } 750 751 unittest 752 { 753 struct A {} 754 755 auto ma = Maybe!A(A()); 756 auto mb = Maybe!A.nothing; 757 758 assert(!ma.isNothing); 759 assert(mb.isNothing); 760 761 assert(ma.get == A()); 762 assertThrown!Error(mb.get); 763 764 bool ncase = false, jcase = false; 765 ma.map(() {ncase = true;}, (v) {jcase = true;}); 766 assert(jcase && !ncase); 767 768 ncase = jcase = false; 769 mb.map(() {ncase = true;}, (v) {jcase = true;}); 770 assert(!jcase && ncase); 771 } 772 773 /** 774 * Transforms delegate into lazy range. Generation is stopped, when 775 * $(B genfunc) returns $(B Maybe!T.nothing). 776 * 777 * Example: 778 * -------- 779 * assert( (() => Maybe!int(1)).generator.take(10).equal(1.repeat.take(10)) ); 780 * assert( (() => Maybe!int.nothing).generator.empty); 781 * assert( (() 782 * { 783 * static size_t i = 0; 784 * return i++ < 10 ? Maybe!int(1) : Maybe!int.nothing; 785 * } 786 * ).generator.equal(1.repeat.take(10))); 787 * 788 * class A {} 789 * auto a = new A(); 790 * 791 * assert( (() => Maybe!A(a)).generator.take(10).equal(a.repeat.take(10)) ); 792 * assert( (() => Maybe!A.nothing).generator.empty); 793 * assert( (() 794 * { 795 * static size_t i = 0; 796 * return i++ < 10 ? Maybe!A(a) : Maybe!A.nothing; 797 * } 798 * ).generator.equal(a.repeat.take(10))); 799 * -------- 800 */ 801 auto generator(T)(Maybe!T delegate() genfunc) 802 { 803 struct Sequencer 804 { 805 private Maybe!T currvalue; 806 807 T front() 808 { 809 assert(!currvalue.isNothing, "Generator range is empty!"); 810 return currvalue.get; 811 } 812 813 bool empty() 814 { 815 return currvalue.isNothing; 816 } 817 818 void popFront() 819 { 820 currvalue = genfunc(); 821 } 822 } 823 static assert(isInputRange!Sequencer); 824 825 auto s = Sequencer(); 826 s.popFront; 827 return s; 828 } 829 unittest 830 { 831 assert( (() => Maybe!int(1)).generator.take(10).equal(1.repeat.take(10)) ); 832 assert( (() => Maybe!int.nothing).generator.empty); 833 assert( (() 834 { 835 static size_t i = 0; 836 return i++ < 10 ? Maybe!int(1) : Maybe!int.nothing; 837 } 838 ).generator.equal(1.repeat.take(10))); 839 840 class A {} 841 auto a = new A(); 842 843 assert( (() => Maybe!A(a)).generator.take(10).equal(a.repeat.take(10)) ); 844 assert( (() => Maybe!A.nothing).generator.empty); 845 assert( (() 846 { 847 static size_t i = 0; 848 return i++ < 10 ? Maybe!A(a) : Maybe!A.nothing; 849 } 850 ).generator.equal(a.repeat.take(10))); 851 } 852 853 private template TypesOf(T...) 854 { 855 static if(T.length == 1) 856 alias TypesOf = typeof(T[0]); 857 else 858 alias TypesOf = TypeTuple!(typeof(T[0]), TypesOf!(T[1..$])); 859 } 860 861 /** 862 * Allows to fast retreiving results from functions that returns a tuple. 863 */ 864 @property void tie(T...)(Tuple!(TypesOf!T) t) 865 { 866 foreach(i, ref var; T) 867 { 868 T[i] = t[i]; 869 } 870 } 871 /// Example 872 unittest 873 { 874 Tuple!(int, string) foo() 875 { 876 return tuple(1, "a"); 877 } 878 879 int x; 880 string y; 881 882 tie!(x,y) = foo(); 883 assert(x == 1 && y == "a"); 884 }