1 module postgresql.value; 2 3 import std.conv: to; 4 import std.array: appender; 5 import std.format: format; 6 import std.datetime: Date, TimeOfDay, DateTime, SysTime; 7 8 import std.bigint; 9 import std.traits; 10 import std.algorithm.comparison: among; 11 12 import postgresql.core; 13 import postgresql.protocol: PGType; 14 15 struct Value 16 { 17 @safe: 18 19 enum Type : ubyte 20 { 21 null_, // null 22 bool_, // bool 23 integer, // long 24 float_, // double 25 bytes, // ubyte[] 26 str, // string 27 array, // Value[] 28 map, // Value[Value] 29 } 30 31 package 32 { 33 PGType _tag; 34 Type _type; 35 bool _bool_; 36 long _integer; 37 double _float_; 38 ubyte[] _bytes; 39 string _str; 40 Value[] _array; 41 Value[Value] _map; 42 SysTime _dateTime; 43 BigInt _bigInt; 44 45 enum warnNotArray = "Container in not 'Value.Type.array'"; 46 enum warnNotMap = "Container in not 'Value.Type.map'"; 47 } 48 49 @property bool isNull() const nothrow @nogc 50 { 51 return _type == Type.null_; 52 } 53 /// ditto 54 unittest 55 { 56 Value v1; 57 auto v2 = Value(null); 58 auto v3 = Value(Value.Type.null_); 59 assert(v1.isNull); 60 assert(v2.isNull); 61 assert(v3.isNull); 62 } 63 64 @property bool isType(string t)() const nothrow @nogc 65 { 66 mixin("return _type == Type." ~ t ~ ";"); 67 } 68 69 @property PGType tag() const nothrow @nogc 70 { 71 return _tag; 72 } 73 /// 74 @property void tag(PGType t) nothrow 75 { 76 _tag = t; 77 } 78 79 @property Type type() const nothrow @nogc 80 { 81 return _type; 82 } 83 /// 84 unittest 85 { 86 assert(Value(true).type == Value.Type.bool_); 87 assert(Value(string[].init).type == Value.Type.array); 88 assert(Value(string[string].init).isType!"map"); 89 // TODO: 90 } 91 92 alias opDollar = length; 93 @property size_t length() const nothrow @nogc 94 { 95 switch(_type) with (Type) 96 { 97 case bytes: return _bytes.length; 98 case str: return _str.length; 99 case array: return _array.length; 100 case map: return _map.length; 101 default: 102 } 103 104 version (CBOR_STRICT) 105 { 106 assert(false); 107 } else { 108 return 0; 109 } 110 } 111 /// ditto 112 unittest 113 { 114 // bytes 115 ubyte[] t = [1, 2, 3, 4, 5, 6, 7, 8, 9]; 116 auto v1 = Value(t); 117 assert(v1.isType!"bytes" && v1.length == 9); 118 119 // string 120 auto v2 = Value("12345678"); 121 assert(v2.isType!"str" && v2.length == 8); 122 123 // array 124 auto v3 = Value([1, 2, 3, 4, 5, 6, 7]); 125 assert(v3.isType!"array" && v3.length == 7); 126 127 // map 128 auto v4 = Value([ 129 "1":"", 130 "2":"", 131 "3":"", 132 "4":"", 133 "5":"", 134 "6":"", 135 ]); 136 assert(v4.isType!"map" && v4.length == 6); 137 } 138 139 this(in Type t) nothrow 140 { 141 _type = t; 142 } 143 /// ditto 144 this(T)(in T other) pure 145 if (isOwnPOD!T || is(Unqual!T == Value)) 146 { 147 save(other); 148 } 149 /// ditto 150 this(T)(in T[] value) pure 151 if (isValue!T && !is(Unqual!T == ubyte)) 152 { 153 _type = Type.array; 154 foreach(v; value) 155 { 156 _array ~= Value(v); 157 } 158 } 159 /// ditto 160 this(K,V)(in V[K] value) pure 161 if ((is(Unqual!K == string) || is(Unqual!K == Value)) && isValue!V) 162 { 163 _type = Type.map; 164 foreach(k, v; value) 165 { 166 _map[Value(k)] = Value(v); 167 } 168 } 169 /// 170 unittest 171 { 172 assert(Value(null).type == Value.Type.null_); 173 assert(Value(true).type == Value.Type.bool_); 174 assert(Value(-123).type == Value.Type.integer); 175 assert(Value(+123).type == Value.Type.integer); 176 assert(Value(3.14).type == Value.Type.float_); 177 assert(Value("ad").type == Value.Type.str); 178 assert(Value(["ab","cd"]).type == Value.Type.array); 179 assert(Value(["ab":"cd"]).type == Value.Type.map); 180 } 181 182 bool opEquals()(auto ref const Value other) const nothrow @nogc 183 { 184 if ( _type != other._type ) 185 { 186 return false; 187 } 188 189 final switch(_type) with (Type) 190 { 191 case null_: return true; 192 case bool_: return _bool_ == other._bool_; 193 case integer: return _integer == other._integer; 194 case float_: return _float_ == other._float_; 195 case bytes: return _bytes == other._bytes; 196 case str: return _str == other._str; 197 case array: return _array == other._array; 198 case map: return _map == other._map; 199 } 200 } 201 /// ditto 202 bool opEquals(T)(in T other) const nothrow @nogc 203 if (isValue!T) 204 { 205 enum t = typeId!T; 206 static if (t == Type.null_) 207 { 208 return _type == Type.null_; 209 } 210 else 211 { 212 mixin("return _type == t && _" ~ t.to!string ~ " == other;"); 213 } 214 } 215 /// ditto 216 unittest 217 { 218 assert(Value(null) != Value(1)); 219 220 // null 221 assert(Value(null) == null); 222 assert(Value(null) == Value(null)); 223 224 // bool 225 assert(Value(false) == false); 226 assert(Value(true) == Value(true)); 227 228 // integer 229 assert(Value(0xDEADC0DE) == 0xDEADC0DE); 230 assert(Value(0X12345678) == Value(0X12345678)); 231 232 // floating point 233 assert(Value(1234.5678) == 1234.5678); 234 assert(Value(9876.5432) == Value(9876.5432)); 235 236 // bytes 237 ubyte[] a = [1,2,3,4]; 238 ubyte[] b = [1,2,3,4]; 239 assert(Value(a) == b); 240 assert(Value(a) == Value(b)); 241 242 // string 243 assert(Value("foo") == "foo"); 244 assert(Value("bar") == Value("bar")); 245 246 // array 247 assert(Value(["foo","bar"]) == Value(["foo","bar"])); 248 249 // map 250 assert(Value(["foo":"bar", "fuz":"buz"]) == Value(["fuz":"buz", "foo":"bar"])); 251 } 252 253 254 @property T opCast(T)() const @trusted 255 { 256 static if( is(T == bool) ) 257 { 258 final switch( _type ) with (Type) 259 { 260 case null_: return false; 261 case bool_: return _bool_; 262 case integer: return _integer != 0; 263 case float_: return _float_ != 0; 264 case bytes: return _bytes.length > 0; 265 case str: return _str.length > 0; 266 case array: return _array.length > 0; 267 case map: return _map.length > 0; 268 } 269 } 270 else static if( 271 is(T == int) || is(T == long) 272 || is(T == float) || is(T == double) 273 || is(T == real)) 274 { 275 final switch( _type ) with (Type) 276 { 277 case null_: return 0; 278 case bool_: return _bool_ ? 1 : 0; 279 case integer: return cast(T) _integer; 280 case float_: return cast(T) _float_; 281 case bytes: return T.init; 282 case str: return _str.to!T; 283 case array: return T.init; 284 case map: return T.init; 285 } 286 } 287 else static if( is(T == ubyte[])) 288 { 289 switch( _type ) with (Type) 290 { 291 default: return cast(T) toString.dup; 292 case null_: return cast(T) null; 293 case bool_: return cast(T) [_bool_ ? 1 : 0]; 294 case bytes: return cast(T) _bytes.dup; 295 case str: return cast(T) _str.dup; 296 } 297 } 298 else static if( is(T == string) ) 299 { 300 switch( _type ) with (Type) 301 { 302 default: return cast(T) toString.dup; 303 case null_: return cast(T) null; 304 case bytes: return cast(T) _bytes.dup; 305 case str: return cast(T) _str.dup; 306 } 307 } 308 else static if( is(T == Value[]) ) 309 { 310 switch( _type ) with (Type) 311 { 312 default: return T.init; 313 case Type.array: return _array.clone; 314 } 315 } 316 else static if( is(T == Value[Value]) ) 317 { 318 switch( _type ){ 319 default: 320 return T.init; 321 case Type.map: 322 return _map; 323 } 324 } 325 else static assert(0, format("%s can't cast to %s", Value.stringof, T.stringof)); 326 } 327 /// 328 unittest 329 { 330 Value v; 331 ubyte[] bytesNil; 332 ubyte[] bytesOne = [1]; 333 Value[] emptyArray; 334 Value[Value] emptyMap; 335 336 v = null; 337 // null to bool 338 assert(cast(bool) v == false); 339 // null to int 340 assert(cast(long) v == 0); 341 // null to float 342 assert(cast(double) v == 0.0); 343 // null to bytes 344 assert(cast(ubyte[]) v == bytesNil); 345 // null to string 346 assert(cast(string) v == ""); 347 // null to array 348 //assert(cast(Value[]) v == emptyArray); 349 350 v = true; 351 // bool to double 352 assert(cast(bool) v == true); 353 assert(cast(double) v == 1.0); 354 // bool to integer 355 assert(cast(long) v == 1); 356 // bool to bytes 357 assert(cast(ubyte[]) v == bytesOne); 358 // bool to string 359 assert(cast(string) v == "true"); 360 // Array 361 //assert(cast(Value[]) v == Value[]); 362 363 v = 3.14; 364 assert(cast(bool) v == true); 365 assert(cast(double) v == 3.14); 366 assert(cast(long) v == 3); 367 assert(cast(string) v == "3.14"); 368 369 v = int(-42); 370 assert(cast(bool) v == true); 371 assert(cast(double) v == -42.0); 372 assert(cast(long) v == -42); 373 assert(cast(string) v == "-42"); 374 375 v = long(42); 376 assert(cast(bool) v == true); 377 assert(cast(double) v == 42.0); 378 assert(cast(long) v == 42); 379 assert(cast(string) v == "42"); 380 381 // bytes 382 ubyte[] buf = [0x61, 0x62, 0x63, 0x64]; 383 v = buf; 384 385 import std.math: isNaN; 386 assert(cast(bool) v == true); 387 assert((cast(double) v).isNaN); 388 assert(cast(long) v == 0); 389 assert(cast(string) v == "abcd"); 390 391 v = "42"; 392 assert(cast(bool) v == true); 393 assert(cast(double) v == 42.0); 394 assert(cast(long) v == 42); 395 assert(cast(string) v == "42"); 396 397 v = "abc"; 398 assert(cast(bool) v == true); 399 assert(cast(string) v == "abc"); 400 401 import std.exception; 402 import std.conv; 403 assertThrown!ConvException(cast(double) v == 0.0); 404 assertThrown!ConvException(cast(long) v == 42); 405 } 406 407 ref Value opAssign(T)(in T other) pure 408 if (isValue!T || is(Unqual!T == Value)) 409 { 410 save(other); 411 return this; 412 } 413 /// 414 unittest 415 { 416 Value v; 417 418 v = null; 419 assert(v.type == Value.Type.null_); 420 421 v = true; 422 assert(v.type == Value.Type.bool_); 423 424 v = int(-42); 425 assert(v.type == Value.Type.integer); 426 427 v = long(42); 428 assert(v.type == Value.Type.integer); 429 430 v = double(3.14); 431 assert(v.type == Value.Type.float_); 432 433 v = "foo"; 434 assert(v.type == Value.Type.str); 435 } 436 437 438 void opOpAssign(string op, T)(T other) pure 439 if (!is(Unqual!T == typeof(null)) && isValue!T) 440 { 441 opOpAssign!op(Value(other)); 442 } 443 /// 444 void opOpAssign(string op)(Value other) pure 445 if (op == "+" || op == "-" || op == "*" || op == "/" || op == "%" || op =="~") 446 { 447 immutable warn = format("Restricted binary operation '%s=' between '%s' and '%s'", op, _type, other._type); 448 449 static if( op.among("+", "-", "*", "/")) 450 { 451 assert(_type == other._type, warn); 452 453 if( _type == Type.integer ) 454 { 455 mixin("_integer " ~ op~ "= other._integer;"); 456 } 457 else if( _type == Type.float_) 458 { 459 mixin("_float_" ~ op ~ "= other._float_;"); 460 } 461 else assert(0, warn); 462 } 463 else static if( op == "~" ) 464 { 465 if (_type == Type.bytes) 466 { 467 assert(_type == other._type, warn); 468 _bytes ~= other._bytes.dup; 469 } 470 else if (_type == Type.str) 471 { 472 assert(_type == other._type, warn); 473 _str ~= other._str.dup; 474 } 475 else if (_type == Type.array) 476 { 477 if (other._type == Type.array) 478 { 479 foreach(v; other._array) 480 { 481 _array ~= v.clone; 482 } 483 } 484 else 485 { 486 _array ~= other.clone; 487 } 488 } 489 else assert(0, warn); 490 } 491 else assert(0, warn); 492 } 493 /// 494 unittest 495 { 496 // Int 497 auto vInt = Value(42); 498 vInt /= 2; 499 assert(vInt == 21); 500 vInt *= 4; 501 assert(vInt == 84); 502 vInt -= 4; 503 assert(vInt == 80); 504 vInt += 20; 505 assert(vInt == 100); 506 507 // Float 508 auto vFload = Value(42.0); 509 vFload -= 1.0; 510 assert(vFload == 41.0); 511 vFload /= 2.0; 512 assert(vFload == 20.5); 513 vFload += 10.0; 514 assert(vFload == 30.5); 515 vFload *= 2.0; 516 assert(vFload == 61.0); 517 518 // bytes 519 ubyte[] b1 = [1, 2]; 520 ubyte[] b2 = [3, 4]; 521 ubyte[] b3 = [1, 2, 3, 4]; 522 auto vBytes = Value(b1); 523 vBytes ~= b2; 524 assert(vBytes == b3); 525 526 // string 527 auto vStr = Value("Hello"); 528 vStr ~= " world!"; 529 assert(vStr == "Hello world!"); 530 531 // Array 532 auto vArray = Value(["abc", "def", "xyz"]); 533 vArray ~= "foo"; 534 assert(vArray == Value(["abc", "def", "xyz", "foo"])); 535 vArray ~= Value(["fuz", "buz"]); 536 assert(vArray == Value(["abc", "def", "xyz", "foo", "fuz", "buz"])); 537 } 538 539 /// Has key 540 bool has(in string key) const nothrow 541 { 542 return has(Value(key)); 543 } 544 /// ditto 545 bool has(Value key) const nothrow 546 { 547 if (_type == Type.map) 548 { 549 return (key in _map) !is null; 550 } 551 else assert(0, warnNotMap); 552 } 553 /// ditto 554 unittest 555 { 556 auto v = Value(["foo":1, "bar":2]); 557 assert(v.has("foo")); 558 assert(!v.has("baz")); 559 assert(v.has(Value("bar"))); 560 } 561 562 Value take(in string key) 563 { 564 return take(Value(key)); 565 } 566 /// ditto 567 Value take(Value key) 568 { 569 // FIXME: const? 570 auto tmp = opIndex(key); 571 remove(key); 572 return tmp; 573 } 574 /// ditto 575 unittest 576 { 577 auto v1 = Value(["a":1, "b":2]); 578 auto v2 = v1.take("b"); 579 assert(v1 == Value(["a":1])); 580 assert(v2 == 2); 581 } 582 583 // Remove value by key 584 void remove(in string key) nothrow 585 { 586 remove(Value(key)); 587 } 588 /// ditto 589 void remove(Value key) nothrow 590 { 591 if (_type == Type.map) 592 { 593 _map.remove(key); 594 } 595 else assert(0, warnNotMap); 596 } 597 /// ditto 598 unittest 599 { 600 auto v1 = Value(["a":1, "b":2]); 601 v1.remove("b"); 602 assert(v1 == Value(["a":1])); 603 } 604 605 Value get(in string key, in string failback) const pure 606 { 607 return get(Value(key), Value(failback)); 608 } 609 /// ditto 610 Value get(in string key, Value failback = Value()) const pure 611 { 612 return get(Value(key), failback); 613 } 614 /// ditto 615 Value get(Value key, Value failback = Value()) const pure 616 { 617 if( auto pv = key in _map) 618 return pv.clone; 619 620 return failback; 621 } 622 /// ditto 623 unittest 624 { 625 auto v1 = Value(["a":1, "b":2]); 626 assert(v1.get("a") == 1); 627 assert(v1.get("b") == 2); 628 assert(v1.get("c").isNull); 629 assert(v1.get("d", "3") == "3"); 630 assert(v1.get("e", Value(3)) == 3); 631 assert(v1.get(Value("f"), Value(4)) == 4); 632 } 633 634 ref inout(Value) opIndex(size_t idx) inout nothrow 635 { 636 if (_type == Type.array) 637 { 638 return _array[idx]; 639 } 640 else assert(0, warnNotArray); 641 } 642 /// 643 unittest 644 { 645 auto value = Value(Value.Type.array); 646 647 value ~= true; 648 value ~= 42; 649 value ~= 3.14; 650 value ~= "foo"; 651 652 assert(value[0] == true); 653 assert(value[1] == 42); 654 assert(value[2] == 3.14); 655 assert(value[3] == "foo"); 656 } 657 658 /// Assign value by key 659 void opIndexAssign(in Value val, in string key) pure 660 { 661 if (_type == Type.map) 662 { 663 _map[Value(key)] = val.clone; 664 } 665 else assert(false, warnNotMap); 666 } 667 /// ditto 668 void opIndexAssign(in Value val, in Value key) pure 669 { 670 if (_type == Type.map) 671 { 672 _map[key.clone] = val.clone; 673 } 674 else assert(false, warnNotMap); 675 } 676 /// ditto 677 void opIndexAssign(T)(T val, in string key) pure 678 if (isValue!T) 679 { 680 if (_type == Type.map) 681 { 682 _map[Value(key)] = Value(val); 683 } 684 else assert(false, warnNotMap); 685 } 686 /// ditto 687 void opIndexAssign(T)(in T val, in Value key) pure 688 if (isValue!T) 689 { 690 if (_type == Type.map) 691 { 692 _map[key.clone] = Value(val); 693 } 694 else assert(false, warnNotMap); 695 } 696 697 /// Get value by key 698 ref inout(Value) opIndex(in string key) inout pure 699 { 700 return opIndex(Value(key)); 701 } 702 /// ditto 703 ref inout(Value) opIndex(in Value key) inout pure 704 { 705 if (_type == Type.map) 706 { 707 return _map[key.clone]; 708 } 709 else assert(false, warnNotMap); 710 } 711 /// 712 unittest 713 { 714 Value v1 = cast(Value[Value]) null; 715 const string abc = "abc"; 716 const Value def = Value("def"); 717 718 v1["abc"] = true; 719 v1[def] = 3.14; 720 v1[Value("xyz")] = Value("foo"); 721 722 723 assert(v1["abc"] == true); 724 assert(v1["def"] == 3.14); 725 assert(v1["xyz"] == "foo"); 726 727 // const 728 const Value v2 = v1; 729 730 assert(v2[abc] == true); 731 assert(v2[def] == 3.14); 732 assert(v2[Value("xyz")] == "foo"); 733 } 734 735 Value opBinary(string op)(in Value other) 736 const { 737 assert(_type == other._type, format("Binary operation '%s' between %s and %s", op, _type, other._type)); 738 static if( op == "&&" || op == "||") 739 { 740 if (_type == Type.bool_) 741 { 742 mixin("return Value(_boolean " ~ op ~ " other._boolean);"); 743 } 744 else assert(0); 745 } 746 else static if( op == "+" || op == "-" || op == "*" || op == "/" || op == "%") 747 { 748 if( _type == Type.integer ) 749 { 750 mixin("return Value(_integer " ~ op ~ " other._integer);"); 751 } 752 else if( _type == Type.float_ ) 753 { 754 mixin("return Value(_float_ " ~ op ~ " other._float_);"); 755 } 756 else assert(false); 757 } 758 else static if( op == "~" ) 759 { 760 if( _type == Type.str ) 761 { 762 return Value(_str ~ other._str); 763 } 764 else if (_type == Type.array) 765 { 766 return Value(_array ~ other._array); 767 } 768 else assert(false); 769 } else static assert(format("Unsupported operator '%s'", op)); 770 } 771 /// ditto 772 unittest 773 { 774 775 } 776 777 mixin(opApply1!""); // opApply(ref val) 778 //mixin(opApply1!"immutable");// opApply(ref immutable val) 779 mixin(opApply1!"const"); // opApply(ref const val) 780 /// 781 unittest 782 { 783 int i = 10; 784 // Array 785 foreach(v; Value([10, 11])) 786 { 787 //assert(v == i++); 788 } 789 790 // Map, ref 791 // One item coz random key order 792 foreach(v; Value(["1":"a"])) 793 { 794 assert(v == "a"); 795 } 796 } 797 ////mixin(opApply2Array!""); // opApply(ref const size_t key, ref val) 798 ////mixin(opApply2Array!"immutable");// opApply(ref const size_t key, ref immutable val) 799 mixin(opApply2Array!"const"); // opApply(ref const size_t key, ref const val) 800 unittest 801 { 802 size_t i; 803 foreach(ref const size_t k, v; Value([10, 11])) 804 { 805 assert(k == i); 806 assert(v == (i++ + 10)); 807 } 808 } 809 ////mixin(opApply2Map!""); // opApply(ref Value) 810 ////mixin(opApply2Map!"immutable");// opApply(ref immutable Value) 811 mixin(opApply2Map!"const"); // opApply(ref const Value) 812 /// 813 unittest 814 { 815 foreach(ref const Value k, v; Value(["1":"1", "2":"2"])) 816 { 817 assert(k == v); 818 } 819 } 820 821 Value[] toArray() const 822 { 823 Value[] ret; 824 if (_type == Type.array) 825 { 826 foreach (i; _array) 827 { 828 ret ~= i.clone; 829 } 830 } 831 return ret; 832 } 833 /// 834 unittest 835 { 836 assert([Value(1), Value(2), Value(3)] == Value([1, 2, 3]).toArray); 837 } 838 839 Value[Value] toMap() const 840 { 841 Value[Value] ret; 842 if (_type == Type.map) 843 { 844 foreach (k, v; _map) 845 { 846 ret[k.clone] = v.clone; 847 } 848 } 849 return ret; 850 } 851 /// 852 unittest 853 { 854 assert([Value("foo"):Value("bar"), 855 Value("fuz"):Value("buz")] == Value(["fuz":"buz", "foo":"bar"]).toMap); 856 } 857 858 string toString() const 859 { 860 // 861 final switch (_type) with (Type) 862 { 863 case null_: return null.stringof; 864 case bool_: return _bool_.to!string; 865 case integer: return _integer.to!string; 866 case float_: return _float_.to!string; 867 case bytes: return _bytes.to!string; 868 case str: return _str.to!string; 869 case array: 870 case map: 871 return this.toJsonString; 872 } 873 assert(0); 874 } 875 /// 876 unittest 877 { 878 Value v; 879 880 v = null; 881 assert(v.toString == "null"); 882 883 v = true; 884 assert(v.toString == "true"); 885 886 v = int(-42); 887 assert(v.toString == "-42"); 888 889 v = long(42); 890 assert(v.toString == "42"); 891 892 v = double(3.14); 893 assert(v.toString == "3.14"); 894 895 ubyte[] t = [1,2,3,4]; 896 v = t; 897 assert(v.toString == "[1, 2, 3, 4]"); 898 899 v = "foo"; 900 assert(v.toString == "foo"); 901 902 v = Value([1, 2, 3, 4]); 903 assert(v.toString == "[1,2,3,4]"); 904 905 v = [Value("data"): Value([1, 2, 3, 4])]; 906 assert(v.toString == "{\"data\":[1,2,3,4]}"); 907 } 908 909 hash_t toHash() const nothrow @trusted 910 { 911 static hash_t getHash(T)(T* v) nothrow @safe 912 { 913 return typeid(T).getHash(v); 914 } 915 916 final switch (_type) with (Type) 917 { 918 case null_: return 0; 919 case bool_: return getHash(&_bool_); 920 case integer: return getHash(&_integer); 921 case float_: return getHash(&_float_); 922 case bytes: return getHash(&_bytes); 923 case str: return getHash(&_str); 924 case array: 925 hash_t ret; 926 foreach (elem; _array) 927 { 928 ret ^= elem.toHash(); 929 } 930 return ret; 931 case map: 932 try 933 { 934 hash_t ret; 935 foreach (ref key, ref value; _map) 936 { 937 ret ^= getHash(&key); 938 ret ^= value.toHash(); 939 } 940 return ret; 941 } catch (Throwable) assert(0); 942 } 943 } 944 /// 945 unittest 946 { 947 ubyte[] t = [1,2,3,4]; 948 assert(Value(null).toHash == Value(null).toHash); 949 assert(Value(true).toHash == Value(true).toHash); 950 assert(Value(false).toHash == Value(false).toHash); 951 assert(Value(-1234567).toHash == Value(-1234567).toHash); 952 assert(Value(+1234567).toHash == Value(+1234567).toHash); 953 assert(Value(3.14).toHash == Value(3.14).toHash); 954 assert(Value(t).toHash == Value(t).toHash); 955 assert(Value("foo").toHash == Value("foo").toHash); 956 assert(Value([1,2,3,4]).toHash == Value([1,2,3,4]).toHash); 957 assert(Value(["foo":"bar", "fuz":"buz"]).toHash == Value(["fuz":"buz","foo":"bar"]).toHash); 958 } 959 960 alias dup = clone; 961 Value clone() const pure 962 { 963 Value other; 964 final switch(_type) with (Type) 965 { 966 case null_: return other.save(null); 967 case bool_: return other.save(_bool_); 968 case integer: return other.save(_integer); 969 case float_: return other.save(_float_); 970 case bytes: return other.save(_bytes); 971 case str: return other.save(_str); 972 case array: return other.save(_array); 973 case map: return other.save(_map); 974 } 975 } 976 /// 977 unittest 978 { 979 Value v; 980 ubyte[] b = [1, 2, 3, 4]; 981 982 v = null; 983 assert(v.clone.isType!"null_"); 984 985 v = true; 986 assert(v.clone.isType!"bool_"); 987 988 v = int(-42); 989 assert(v.clone.isType!"integer"); 990 991 v = long(42); 992 assert(v.clone.isType!"integer"); 993 994 v = double(3.14); 995 assert(v.clone.isType!"float_"); 996 997 v = b; 998 assert(v.clone.isType!"bytes" && v == b); 999 1000 v = "foo"; 1001 assert(v.clone.isType!"str"); 1002 1003 v = Value(["abc", "def", "xyz"]); 1004 assert(v.clone.isType!"array" && v == Value(["abc", "def", "xyz"])); 1005 1006 v = Value(["foo":"bar"]); 1007 assert(v.clone.isType!"map" && v == Value(["foo":"bar"])); 1008 } 1009 1010 private Value save(T)(in T other) pure 1011 { 1012 static if (is(Unqual!T == Value)) 1013 { 1014 final switch(other._type) with (Type) 1015 { 1016 case null_: return save(null); 1017 case bool_: return save(other._bool_); 1018 case integer: return save(other._integer); 1019 case float_: return save(other._float_); 1020 case bytes: return save(other._bytes); 1021 case str: return save(other._str); 1022 case array: return save(other._array); 1023 case map: return save(other._map); 1024 } 1025 } 1026 else 1027 { 1028 enum t = typeId!T; 1029 _type = t; 1030 1031 _array.length = 0; 1032 _map = null; 1033 1034 static if (t == Type.null_) 1035 { 1036 // Nothing 1037 } 1038 else static if (t == Type.bytes || t == Type.str) 1039 { 1040 mixin("_" ~ t.to!string ~ " = other.dup;"); 1041 } 1042 else static if (t == Type.array) 1043 { 1044 _array = null; 1045 foreach(ref v; other) 1046 { 1047 _array ~= v.clone; 1048 } 1049 } 1050 else static if (t == Type.map) 1051 { 1052 _map = null; 1053 foreach(k, v; other) 1054 { 1055 _map[Value(k)] = v.clone; 1056 } 1057 } 1058 else static if (t != Type.null_) 1059 { 1060 mixin("_" ~ t.to!string ~ " = other;"); 1061 } 1062 else assert(0, "Not implemented save for type " ~ T.stringof); 1063 return this; 1064 } 1065 } 1066 1067 1068 static @property Type typeId(T)() nothrow @nogc 1069 { 1070 static if( is(Unqual!T == typeof(null)) ) return Type.null_; 1071 else static if( is(Unqual!T == bool) ) return Type.bool_; 1072 else static if( isIntegral!T) return Type.integer; 1073 else static if( isFloatingPoint!T ) return Type.float_; 1074 else static if( is(Unqual!T == ubyte[]) ) return Type.bytes; 1075 else static if( is(Unqual!T == const(ubyte)[])) return Type.bytes; 1076 else static if( is(Unqual!T == string) ) return Type.str; 1077 else static if( is(Unqual!T == Value[]) ) return Type.array; 1078 else static if( is(Unqual!T == const(Value)[])) return Type.array; 1079 else static if( is(Unqual!T == Value[Value]) ) return Type.map; 1080 else static if( is(Unqual!T == const(Value)[Value])) return Type.map; 1081 else static if( is(Unqual!T == Value[string])) return Type.map; 1082 else static if( is(Unqual!T == const(Value)[string])) return Type.map; 1083 else static assert(false, "Unsupported type '%s'".format(T.stringof)); 1084 } 1085 /// 1086 unittest 1087 { 1088 assert(Value.typeId!bool == Type.bool_); 1089 assert(Value.typeId!string == Type.str); 1090 assert(Value.typeId!byte == Type.integer); 1091 assert(Value.typeId!ubyte == Type.integer); 1092 assert(Value.typeId!short == Type.integer); 1093 assert(Value.typeId!ushort == Type.integer); 1094 assert(Value.typeId!int == Type.integer); 1095 assert(Value.typeId!uint == Type.integer); 1096 assert(Value.typeId!long == Type.integer); 1097 assert(Value.typeId!ulong == Type.integer); 1098 assert(Value.typeId!float == Type.float_); 1099 assert(Value.typeId!double == Type.float_); 1100 assert(Value.typeId!real == Type.float_); 1101 assert(Value.typeId!(Value[Value]) == Type.map); 1102 // TODO: 1103 } 1104 } 1105 1106 // Helpers 1107 Value getValue(T)(ref const (Value[string]) map, string key, lazy T defVal = null) 1108 if (isValue!T) 1109 { 1110 if( auto pv = key in map) 1111 { 1112 return pv.clone; 1113 } 1114 return Value(defVal); 1115 } 1116 1117 Value getValue(K,T)(ref const (Value[Value]) map, string key, lazy T defVal = null) 1118 if (isValue!T) 1119 { 1120 if( auto pv = Value(key) in map) 1121 { 1122 return pv.clone; 1123 } 1124 return Value(defVal); 1125 } 1126 1127 1128 Value[] valueArray(T...)(T args) 1129 { 1130 Value[] result; 1131 foreach(arg; args) 1132 { 1133 result ~= Value(arg); 1134 } 1135 return result; 1136 } 1137 unittest 1138 { 1139 auto a1 = valueArray(null, false, 42, 3.14, "foo", Value("false")); 1140 assert(a1[0] == null); 1141 assert(a1[1] == false); 1142 assert(a1[2] == 42); 1143 assert(a1[3] == 3.14); 1144 assert(a1[4] == "foo"); 1145 assert(a1[5] == "false"); 1146 1147 auto a2 = valueArray(false, true, a1); 1148 1149 assert(a2[0] == false); 1150 assert(a2[1] == true); 1151 assert(a2[2] == a1); 1152 } 1153 1154 1155 string toJsonString(in Value val) @trusted 1156 { 1157 import std.json; 1158 1159 JSONValue _toJson(in Value src) 1160 { 1161 final switch (src.type) with (Value.Type) 1162 { 1163 case null_: 1164 return JSONValue(null); 1165 case bool_: 1166 return JSONValue(src._bool_); 1167 case integer: 1168 return JSONValue(src._integer); 1169 case float_: 1170 return JSONValue(src._float_); 1171 case bytes: 1172 case str: 1173 return JSONValue(src.to!string); 1174 case array: { 1175 JSONValue[] vals; 1176 foreach (val; src._array) 1177 { 1178 vals ~= _toJson(val); 1179 } 1180 return JSONValue(vals); 1181 } 1182 case map: { 1183 JSONValue[string] vals; 1184 foreach (key, val; src._map) 1185 { 1186 vals[key.to!string] = _toJson(val); 1187 } 1188 return JSONValue(vals); 1189 } 1190 } 1191 } 1192 1193 return _toJson(val).toString(); 1194 } 1195 /// 1196 unittest 1197 { 1198 assert(`{"a":null}` == Value(["a": Value(null)]).toJsonString); 1199 assert(`{"a":true}` == Value(["a": true]).toJsonString); 1200 assert(`{"a":false}` == Value(["a": false]).toJsonString); 1201 //Precision mismatch 1202 //assert(`{"a":3.14}` == Value(["a": 3.14]).toJsonString); 1203 assert(`{"a":42}` == Value(["a": 42]).toJsonString); 1204 assert(`{"a":[1,2,3,4]}` == Value(["a": Value([1,2,3,4])]).toJsonString); 1205 } 1206 1207 1208 Value fromJsonString(in string s) @trusted 1209 { 1210 import std.json; 1211 1212 Value _(in JSONValue src) 1213 { 1214 final switch (src.type) with (JSON_TYPE) 1215 { 1216 case NULL: return Value(null); 1217 case TRUE: return Value(true); 1218 case FALSE: return Value(false); 1219 case UINTEGER: return Value(cast(int) src.uinteger); 1220 case INTEGER: return Value(src.integer); 1221 case FLOAT: return Value(src.floating); 1222 case STRING: return Value(src.str); 1223 case ARRAY: { 1224 Value[] vals; 1225 foreach (val; src.array) 1226 { 1227 vals ~= _(val); 1228 } 1229 return Value(vals); 1230 } 1231 case OBJECT: { 1232 Value[string] vals; 1233 foreach (key, val; src.object) 1234 { 1235 vals[key] = _(val); 1236 } 1237 return Value(vals); 1238 } 1239 } 1240 } 1241 1242 return _(parseJSON(s)); 1243 } 1244 /// 1245 unittest 1246 { 1247 assert(fromJsonString(`{"a":null}`) == Value(["a": Value(null)])); 1248 assert(fromJsonString(`{"a":true}`) == Value(["a": true])); 1249 assert(fromJsonString(`{"a":false}`) == Value(["a": false])); 1250 assert(fromJsonString(`{"a":3.14}`) == Value(["a": 3.14])); 1251 assert(fromJsonString(`{"a":42}`) == Value(["a": 42])); 1252 assert(fromJsonString(`{"a":[1,2,3,4]}`) == Value(["a": Value([1,2,3,4])])); 1253 } 1254 1255 private enum bool isOwnPOD(T) = is(Unqual!T == typeof(null)) 1256 || is(Unqual!T == bool) 1257 || isIntegral!T 1258 || isFloatingPoint!T 1259 || is(Unqual!T == ubyte[]) 1260 || is(Unqual!T == const(ubyte)[]) 1261 || is(Unqual!T == string); 1262 1263 1264 private enum bool isValue(T) = isOwnPOD!T 1265 || is(Unqual!T == Value) 1266 || is(Unqual!T == Value[]) 1267 || is(Unqual!T == const(Value)[]) 1268 || is(Unqual!T == Value[string]) 1269 || is(Unqual!T == const(Value)[string]) 1270 || is(Unqual!T == Value[Value]) 1271 || is(Unqual!T == const(Value)[Value]) 1272 ; 1273 1274 1275 1276 private template opApply1(string t) 1277 { 1278 const char[] opApply1 = ` 1279 int opApply(scope int delegate(ref ` ~ t ~ ` Value) dg) ` ~ t ~ ` @trusted 1280 { 1281 if ( _type == Type.array ) 1282 { 1283 foreach(ref ` ~ t ~ ` v; _array ) 1284 { 1285 if( auto ret = dg(v) ) 1286 { 1287 return ret; 1288 } 1289 } 1290 return 0; 1291 } 1292 else if ( _type == Type.map ) 1293 { 1294 foreach(ref ` ~ t ~ ` v; _map ) 1295 { 1296 if( auto ret = dg(v) ) 1297 { 1298 return ret; 1299 } 1300 } 1301 return 0; 1302 } 1303 else assert(0); 1304 } 1305 `; 1306 } 1307 1308 private template opApply2Array(string t) 1309 { 1310 const char[] opApply2Array = ` 1311 int opApply(scope int delegate(ref const size_t, ref ` ~ t ~ ` Value) dg) ` ~ t ~ ` @trusted 1312 { 1313 if ( _type == Type.array ) 1314 { 1315 foreach ( ref const idx, ref ` ~ t ~ ` v; _array ) 1316 { 1317 if ( auto ret = dg(idx, v) ) 1318 { 1319 return ret; 1320 } 1321 } 1322 return 0; 1323 } 1324 else assert(0); 1325 } 1326 `; 1327 } 1328 1329 private template opApply2Map(string t) 1330 { 1331 const char[] opApply2Map = ` 1332 int opApply(scope int delegate(ref const Value, ref `~t~` Value) dg) `~t~` @trusted 1333 { 1334 if ( _type == Type.map ) 1335 { 1336 foreach ( ref const idx, ref `~t~` v; _map ) 1337 { 1338 if ( auto ret = dg(idx, v) ) 1339 { 1340 return ret; 1341 } 1342 } 1343 return 0; 1344 } 1345 else assert(0); 1346 } 1347 `; 1348 }