1 module dutils.data.bson; 2 3 // TODO: support nested structs and arrays 4 void populateFromBSON(T)(ref T object, ref BSON data) { 5 import std.conv : to; 6 import std.traits : isSomeFunction, hasMember; 7 import dutils.validation.validate : validate, ValidationError, ValidationErrors; 8 import dutils.validation.constraints : ValidationErrorTypes; 9 10 ValidationError[] errors; 11 12 static foreach (memberName; __traits(allMembers, T)) { 13 static if (!isSomeFunction!(__traits(getMember, T, memberName))) { 14 try { 15 // static if (is(typeof(__traits(getMember, T, memberName)) == EventMeta)) { 16 static if (hasMember!(typeof(__traits(getMember, T, memberName)), 17 "fromBSON") && isSomeFunction!(__traits(getMember, 18 __traits(getMember, T, memberName), "fromBSON"))) { 19 __traits(getMember, object, memberName) = __traits(getMember, 20 __traits(getMember, T, memberName), "fromBSON")(data[memberName]); 21 } else { 22 if (data[memberName].type != BSON.Type.null_) { 23 __traits(getMember, object, memberName) = data[memberName].get!(typeof(__traits(getMember, 24 T, memberName))); 25 } 26 } 27 } catch (Exception error) { 28 errors ~= ValidationError(memberName, ValidationErrorTypes.type, 29 "Value must be convertable to type " ~ typeof(__traits(getMember, 30 T, memberName)).stringof ~ " but got " ~ data[memberName].type.to!string); 31 } 32 } 33 } 34 35 try { 36 validate(object); 37 } catch (ValidationErrors validation) { 38 errors ~= validation.errors; 39 } 40 41 if (errors.length > 0) { 42 throw new ValidationErrors(errors); 43 } 44 } 45 46 /** 47 * populateFromBSON - ensure that population works with valid BSON data 48 */ 49 unittest { 50 import dutils.validation.constraints : ValidateMinimumLength, 51 ValidateMaximumLength, ValidateMinimum, ValidateEmail, ValidateRequired; 52 53 struct Person { 54 @ValidateMinimumLength(2) 55 @ValidateMaximumLength(100) 56 string name; 57 58 @ValidateMinimum!double(20) double height; 59 60 @ValidateEmail() 61 @ValidateRequired() 62 string email; 63 64 @ValidateRequired() 65 bool member; 66 } 67 68 auto data = BSON([ 69 "does not exists": BSON(true), 70 "name": BSON("Anna"), 71 "height": BSON(170.1), 72 "email": BSON("anna@example.com"), 73 "member": BSON(true) 74 ]); 75 76 Person person; 77 populateFromBSON(person, data); 78 79 assert(person.name == "Anna", "expected name Anna"); 80 assert(person.height == 170.1, "expected height 170"); 81 assert(person.email == "anna@example.com", "expected email anna@example.com"); 82 assert(person.member == true, "expected member true"); 83 } 84 85 /** 86 * populateFromBSON - ensure that validation errors are thrown with invalid BSON data 87 */ 88 unittest { 89 import dutils.validation.validate : ValidationError, ValidationErrors; 90 import dutils.validation.constraints : ValidationErrorTypes, ValidateMinimumLength, 91 ValidateMaximumLength, ValidateMinimum, ValidateEmail, ValidateRequired; 92 93 struct Person { 94 @ValidateMinimumLength(2) 95 @ValidateMaximumLength(100) 96 string name; 97 98 @ValidateMinimum!double(20) double height; 99 100 @ValidateEmail() 101 @ValidateRequired() 102 string email; 103 104 @ValidateRequired() 105 bool member; 106 } 107 108 auto data = BSON([ 109 "does not exists": BSON(true), 110 "name": BSON("Anna"), 111 "height": BSON("not a number") 112 ]); 113 114 Person person; 115 116 auto catched = false; 117 try { 118 populateFromBSON(person, data); 119 } catch (ValidationErrors validation) { 120 import std.conv : to; 121 122 catched = true; 123 assert(validation.errors.length == 4, 124 "expected 4 errors, got " ~ validation.errors.length.to!string 125 ~ " with message: " ~ validation.msg); 126 assert(validation.errors[0].type == "type", "expected minimumLength error"); 127 assert(validation.errors[0].path == "height", "expected error path to be height"); 128 assert(validation.errors[1].type == "minimum", "expected minimum error"); 129 assert(validation.errors[1].path == "height", "expected error path to be height"); 130 assert(validation.errors[2].type == "required", "expected required error"); 131 assert(validation.errors[2].path == "email", "expected error path to be email"); 132 assert(validation.errors[3].type == "required", "expected required error"); 133 assert(validation.errors[3].path == "member", "expected error path to be member"); 134 } 135 136 assert(catched == true, "did not catch the expected errors"); 137 } 138 139 /** 140 * populateFromBSON - should populate 141 */ 142 unittest { 143 import dutils.validation.constraints : ValidateRequired, ValidateEmail; 144 145 struct Email { 146 @ValidateRequired() 147 @ValidateEmail() 148 string to; 149 150 @ValidateEmail() 151 string from; 152 153 string subject; 154 155 @ValidateRequired() 156 string body; 157 } 158 159 auto data = BSON([ 160 "does not exists": BSON(true), 161 "to": BSON("anna@example.com"), 162 "body": BSON("Some text") 163 ]); 164 165 Email email; 166 populateFromBSON(email, data); 167 168 assert(email.to == "anna@example.com", "expected to to be anna@example.com"); 169 assert(email.from == "", "expected from to be \"\""); 170 assert(email.subject == "", "expected from to be \"\""); 171 assert(email..body == "Some text", "expected from to be \"Some text\""); 172 } 173 174 /** 175 BSON serialization and value handling. 176 177 Copyright: © 2012-2015 Sönke Ludwig 178 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 179 Authors: Sönke Ludwig 180 */ 181 182 import dutils.data.json; 183 184 /// 185 unittest { 186 void manipulateBSON(BSON b) { 187 import std.stdio; 188 189 // retrieving the values is done using get() 190 assert(b["name"].get!string == "Example"); 191 assert(b["id"].get!int == 1); 192 193 // semantic conversions can be done using to() 194 assert(b["id"].to!string == "1"); 195 196 // prints: 197 // name: "Example" 198 // id: 1 199 foreach (string key, value; b) 200 writefln("%s: %s", key, value); 201 202 // print out with JSON syntax: {"name": "Example", "id": 1} 203 writefln("BSON: %s", b.toString()); 204 } 205 } 206 207 /// Constructing `BSON` objects 208 unittest { 209 // construct a BSON object {"field1": "foo", "field2": 42, "field3": true} 210 211 // using the constructor 212 BSON b1 = BSON([ 213 "field1": BSON("foo"), 214 "field2": BSON(42), 215 "field3": BSON(true) 216 ]); 217 218 // using piecewise construction 219 BSON b2 = BSON.emptyObject; 220 b2["field1"] = "foo"; 221 b2["field2"] = 42; 222 b2["field3"] = true; 223 224 // using serialization 225 struct S { 226 string field1; 227 int field2; 228 bool field3; 229 } 230 231 BSON b3 = S("foo", 42, true).serializeToBSON(); 232 } 233 234 import std.algorithm; 235 import std.array; 236 import std.base64; 237 import std.bitmanip; 238 import std.conv; 239 import std.datetime; 240 import std.uuid : UUID; 241 import std.exception; 242 import std.range; 243 import std.traits; 244 import std.typecons : Tuple, tuple; 245 246 alias bdata_t = immutable(ubyte)[]; 247 248 /** 249 Represents a BSON value. 250 251 252 */ 253 struct BSON { 254 @safe: 255 256 /// Represents the type of a BSON value 257 enum Type : ubyte { 258 end = 0x00, /// End marker - should never occur explicitly 259 double_ = 0x01, /// A 64-bit floating point value 260 string = 0x02, /// A UTF-8 string 261 object = 0x03, /// An object aka. dictionary of string to BSON 262 array = 0x04, /// An array of BSON values 263 binData = 0x05, /// Raw binary data (ubyte[]) 264 undefined = 0x06, /// Deprecated 265 objectID = 0x07, /// BSON Object ID (96-bit) 266 bool_ = 0x08, /// Boolean value 267 date = 0x09, /// Date value (UTC) 268 null_ = 0x0A, /// Null value 269 regex = 0x0B, /// Regular expression 270 dbRef = 0x0C, /// Deprecated 271 code = 0x0D, /// JaveScript code 272 symbol = 0x0E, /// Symbol/variable name 273 codeWScope = 0x0F, /// JavaScript code with scope 274 int_ = 0x10, /// 32-bit integer 275 timestamp = 0x11, /// Timestamp value 276 long_ = 0x12, /// 64-bit integer 277 minKey = 0xff, /// Internal value 278 maxKey = 0x7f, /// Internal value 279 } 280 281 /// Returns a new, empty BSON value of type Object. 282 static @property BSON emptyObject() { 283 return BSON(cast(BSON[string]) null); 284 } 285 286 /// Returns a new, empty BSON value of type Array. 287 static @property BSON emptyArray() { 288 return BSON(cast(BSON[]) null); 289 } 290 291 private { 292 Type m_type = Type.undefined; 293 bdata_t m_data; 294 } 295 296 /** 297 Creates a new BSON value using raw data. 298 299 A slice of the first bytes of `data` is stored, containg the data related to the value. An 300 exception is thrown if `data` is too short. 301 */ 302 this(Type type, bdata_t data) { 303 m_type = type; 304 m_data = data; 305 final switch (type) { 306 case Type.end: 307 m_data = null; 308 break; 309 case Type.double_: 310 m_data = m_data[0 .. 8]; 311 break; 312 case Type..string: 313 m_data = m_data[0 .. 4 + fromBSONData!int(m_data)]; 314 break; 315 case Type.object: 316 m_data = m_data[0 .. fromBSONData!int(m_data)]; 317 break; 318 case Type.array: 319 m_data = m_data[0 .. fromBSONData!int(m_data)]; 320 break; 321 case Type.binData: 322 m_data = m_data[0 .. 5 + fromBSONData!int(m_data)]; 323 break; 324 case Type.undefined: 325 m_data = null; 326 break; 327 case Type.objectID: 328 m_data = m_data[0 .. 12]; 329 break; 330 case Type.bool_: 331 m_data = m_data[0 .. 1]; 332 break; 333 case Type.date: 334 m_data = m_data[0 .. 8]; 335 break; 336 case Type.null_: 337 m_data = null; 338 break; 339 case Type.regex: 340 auto tmp = m_data; 341 tmp.skipCString(); 342 tmp.skipCString(); 343 m_data = m_data[0 .. $ - tmp.length]; 344 break; 345 case Type.dbRef: 346 m_data = m_data[0 .. 0]; 347 assert(false, "Not implemented."); 348 case Type.code: 349 m_data = m_data[0 .. 4 + fromBSONData!int(m_data)]; 350 break; 351 case Type.symbol: 352 m_data = m_data[0 .. 4 + fromBSONData!int(m_data)]; 353 break; 354 case Type.codeWScope: 355 m_data = m_data[0 .. 0]; 356 assert(false, "Not implemented."); 357 case Type.int_: 358 m_data = m_data[0 .. 4]; 359 break; 360 case Type.timestamp: 361 m_data = m_data[0 .. 8]; 362 break; 363 case Type.long_: 364 m_data = m_data[0 .. 8]; 365 break; 366 case Type.minKey: 367 m_data = null; 368 break; 369 case Type.maxKey: 370 m_data = null; 371 break; 372 } 373 } 374 375 /** 376 Initializes a new BSON value from the given D type. 377 */ 378 this(double value) { 379 opAssign(value); 380 } 381 /// ditto 382 this(string value, Type type = Type..string) { 383 assert(type == Type..string || type == Type.code || type == Type.symbol); 384 opAssign(value); 385 m_type = type; 386 } 387 /// ditto 388 this(in BSON[string] value) { 389 opAssign(value); 390 } 391 /// ditto 392 this(in BSON[] value) { 393 opAssign(value); 394 } 395 /// ditto 396 this(in BSONBinData value) { 397 opAssign(value); 398 } 399 /// ditto 400 this(in BSONObjectID value) { 401 opAssign(value); 402 } 403 /// ditto 404 this(bool value) { 405 opAssign(value); 406 } 407 /// ditto 408 this(in BSONDate value) { 409 opAssign(value); 410 } 411 /// ditto 412 this(typeof(null)) { 413 opAssign(null); 414 } 415 /// ditto 416 this(in BSONRegex value) { 417 opAssign(value); 418 } 419 /// ditto 420 this(int value) { 421 opAssign(value); 422 } 423 /// ditto 424 this(in BSONTimestamp value) { 425 opAssign(value); 426 } 427 /// ditto 428 this(long value) { 429 opAssign(value); 430 } 431 /// ditto 432 this(in JSON value) { 433 opAssign(value); 434 } 435 /// ditto 436 this(in UUID value) { 437 opAssign(value); 438 } 439 440 /** 441 Assigns a D type to a BSON value. 442 */ 443 void opAssign(in BSON other) { 444 m_data = other.m_data; 445 m_type = other.m_type; 446 } 447 /// ditto 448 void opAssign(double value) { 449 m_data = toBSONData(value).idup; 450 m_type = Type.double_; 451 } 452 /// ditto 453 void opAssign(string value) { 454 import std.utf; 455 456 debug std.utf.validate(value); 457 auto app = appender!bdata_t(); 458 app.put(toBSONData(cast(int) value.length + 1)); 459 app.put(cast(bdata_t) value); 460 app.put(cast(ubyte) 0); 461 m_data = app.data; 462 m_type = Type..string; 463 } 464 /// ditto 465 void opAssign(in BSON[string] value) { 466 auto app = appender!bdata_t(); 467 foreach (k, ref v; value) { 468 app.put(cast(ubyte) v.type); 469 putCString(app, k); 470 app.put(v.data); 471 } 472 473 auto dapp = appender!bdata_t(); 474 dapp.put(toBSONData(cast(int) app.data.length + 5)); 475 dapp.put(app.data); 476 dapp.put(cast(ubyte) 0); 477 m_data = dapp.data; 478 m_type = Type.object; 479 } 480 /// ditto 481 void opAssign(in BSON[] value) { 482 auto app = appender!bdata_t(); 483 foreach (i, ref v; value) { 484 app.put(v.type); 485 putCString(app, to!string(i)); 486 app.put(v.data); 487 } 488 489 auto dapp = appender!bdata_t(); 490 dapp.put(toBSONData(cast(int) app.data.length + 5)); 491 dapp.put(app.data); 492 dapp.put(cast(ubyte) 0); 493 m_data = dapp.data; 494 m_type = Type.array; 495 } 496 /// ditto 497 void opAssign(in BSONBinData value) { 498 auto app = appender!bdata_t(); 499 app.put(toBSONData(cast(int) value.rawData.length)); 500 app.put(value.type); 501 app.put(value.rawData); 502 503 m_data = app.data; 504 m_type = Type.binData; 505 } 506 /// ditto 507 void opAssign(in BSONObjectID value) { 508 m_data = value.m_bytes.idup; 509 m_type = Type.objectID; 510 } 511 /// ditto 512 void opAssign(bool value) { 513 m_data = [value ? 0x01 : 0x00]; 514 m_type = Type.bool_; 515 } 516 /// ditto 517 void opAssign(in BSONDate value) { 518 m_data = toBSONData(value.m_time).idup; 519 m_type = Type.date; 520 } 521 /// ditto 522 void opAssign(typeof(null)) { 523 m_data = null; 524 m_type = Type.null_; 525 } 526 /// ditto 527 void opAssign(in BSONRegex value) { 528 auto app = appender!bdata_t(); 529 putCString(app, value.expression); 530 putCString(app, value.options); 531 m_data = app.data; 532 m_type = type.regex; 533 } 534 /// ditto 535 void opAssign(int value) { 536 m_data = toBSONData(value).idup; 537 m_type = Type.int_; 538 } 539 /// ditto 540 void opAssign(in BSONTimestamp value) { 541 m_data = toBSONData(value.m_time).idup; 542 m_type = Type.timestamp; 543 } 544 /// ditto 545 void opAssign(long value) { 546 m_data = toBSONData(value).idup; 547 m_type = Type.long_; 548 } 549 /// ditto 550 void opAssign(in JSON value) @trusted { 551 auto app = appender!bdata_t(); 552 m_type = writeBSON(app, value); 553 m_data = app.data; 554 } 555 /// ditto 556 void opAssign(in UUID value) { 557 opAssign(BSONBinData(BSONBinData.Type.uuid, value.data.idup)); 558 } 559 560 /** 561 Returns the BSON type of this value. 562 */ 563 @property Type type() const { 564 return m_type; 565 } 566 567 bool isNull() const { 568 return m_type == Type.null_; 569 } 570 571 /** 572 Returns the raw data representing this BSON value (not including the field name and type). 573 */ 574 @property bdata_t data() const { 575 return m_data; 576 } 577 578 /** 579 Converts the BSON value to a D value. 580 581 If the BSON type of the value does not match the D type, an exception is thrown. 582 583 See_Also: `deserializeBSON`, `opt` 584 */ 585 T opCast(T)() const { 586 return get!T(); 587 } 588 /// ditto 589 @property T get(T)() const { 590 static if (is(T == double)) { 591 checkType(Type.double_); 592 return fromBSONData!double(m_data); 593 } else static if (is(T == string)) { 594 checkType(Type..string, Type.code, Type.symbol); 595 return cast(string) m_data[4 .. 4 + fromBSONData!int(m_data) - 1]; 596 } else static if (is(Unqual!T == BSON[string]) || is(Unqual!T == const(BSON)[string])) { 597 checkType(Type.object); 598 BSON[string] ret; 599 auto d = m_data[4 .. $]; 600 while (d.length > 0) { 601 auto tp = cast(Type) d[0]; 602 if (tp == Type.end) 603 break; 604 d = d[1 .. $]; 605 auto key = skipCString(d); 606 auto value = BSON(tp, d); 607 d = d[value.data.length .. $]; 608 609 ret[key] = value; 610 } 611 return cast(T) ret; 612 } else static if (is(Unqual!T == BSON[]) || is(Unqual!T == const(BSON)[])) { 613 checkType(Type.array); 614 BSON[] ret; 615 auto d = m_data[4 .. $]; 616 while (d.length > 0) { 617 auto tp = cast(Type) d[0]; 618 if (tp == Type.end) 619 break; 620 /*auto key = */ 621 skipCString(d); // should be '0', '1', ... 622 auto value = BSON(tp, d); 623 d = d[value.data.length .. $]; 624 625 ret ~= value; 626 } 627 return cast(T) ret; 628 } else static if (is(T == BSONBinData)) { 629 checkType(Type.binData); 630 auto size = fromBSONData!int(m_data); 631 auto type = cast(BSONBinData.Type) m_data[4]; 632 return BSONBinData(type, m_data[5 .. 5 + size]); 633 } else static if (is(T == BSONObjectID)) { 634 checkType(Type.objectID); 635 return BSONObjectID(m_data[0 .. 12]); 636 } else static if (is(T == bool)) { 637 checkType(Type.bool_); 638 return m_data[0] != 0; 639 } else static if (is(T == BSONDate)) { 640 checkType(Type.date); 641 return BSONDate(fromBSONData!long(m_data)); 642 } else static if (is(T == BSONRegex)) { 643 checkType(Type.regex); 644 auto d = m_data[0 .. $]; 645 auto expr = skipCString(d); 646 auto options = skipCString(d); 647 return BSONRegex(expr, options); 648 } else static if (is(T == int)) { 649 checkType(Type.int_); 650 return fromBSONData!int(m_data); 651 } else static if (is(T == BSONTimestamp)) { 652 checkType(Type.timestamp); 653 return BSONTimestamp(fromBSONData!long(m_data)); 654 } else static if (is(T == long)) { 655 checkType(Type.long_); 656 return fromBSONData!long(m_data); 657 } else static if (is(T == JSON)) { 658 pragma(msg, 659 "BSON.get!JSON() and BSON.opCast!JSON() will soon be removed. Please use BSON.toJSON() instead."); 660 return this.toJSON(); 661 } else static if (is(T == UUID)) { 662 checkType(Type.binData); 663 auto bbd = this.get!BSONBinData(); 664 enforce(bbd.type == BSONBinData.Type.uuid, 665 "BSONBinData value is type '" ~ to!string(bbd.type) ~ "', expected to be uuid"); 666 const ubyte[16] b = bbd.rawData; 667 return UUID(b); 668 } else static if (is(T == SysTime)) { 669 checkType(Type.date); 670 return BSONDate(fromBSONData!long(m_data)).toSysTime(); 671 } else 672 static assert(false, "Cannot cast " ~ typeof(this).stringof ~ " to '" ~ T.stringof ~ "'."); 673 } 674 675 /** Returns the native type for this BSON if it matches the current runtime type. 676 677 If the runtime type does not match the given native type, the 'def' parameter is returned 678 instead. 679 */ 680 T opt(T)(T def = T.init) { 681 if (isNull()) 682 return def; 683 try 684 return cast(T) this; 685 catch (Exception e) 686 return def; 687 } 688 /// ditto 689 const(T) opt(T)(const(T) def = const(T).init) const { 690 if (isNull()) 691 return def; 692 try 693 return cast(T) this; 694 catch (Exception e) 695 return def; 696 } 697 698 /** Returns the length of a BSON value of type String, Array, Object or BinData. 699 */ 700 @property size_t length() const { 701 switch (m_type) { 702 default: 703 enforce(false, "BSON objects of type " ~ to!string(m_type) ~ " do not have a length field."); 704 break; 705 case Type..string, Type.code, Type.symbol: 706 return (cast(string) this).length; 707 case Type.array: 708 return byValue.walkLength; 709 case Type.object: 710 return byValue.walkLength; 711 case Type.binData: 712 assert(false); //return (cast(BSONBinData)this).length; break; 713 } 714 assert(false); 715 } 716 717 /** Converts a given JSON value to the corresponding BSON value. 718 */ 719 static BSON fromJSON(in JSON value) @trusted { 720 auto app = appender!bdata_t(); 721 auto tp = writeBSON(app, value); 722 return BSON(tp, app.data); 723 } 724 725 /** Converts a BSON value to a JSON value. 726 727 All BSON types that cannot be exactly represented as JSON, will 728 be converted to a string. 729 */ 730 JSON toJSON() const { 731 switch (this.type) { 732 default: 733 assert(false); 734 case BSON.Type.double_: 735 return JSON(get!double()); 736 case BSON.Type..string: 737 return JSON(get!string()); 738 case BSON.Type.object: 739 JSON[string] ret; 740 foreach (k, v; this.byKeyValue) 741 ret[k] = v.toJSON(); 742 return JSON(ret); 743 case BSON.Type.array: 744 auto ret = new JSON[this.length]; 745 foreach (i, v; this.byIndexValue) 746 ret[i] = v.toJSON(); 747 return JSON(ret); 748 case BSON.Type.binData: 749 return JSON(() @trusted { 750 return cast(string) Base64.encode(get!BSONBinData.rawData); 751 }()); 752 case BSON.Type.objectID: 753 return JSON(get!BSONObjectID().toString()); 754 case BSON.Type.bool_: 755 return JSON(get!bool()); 756 case BSON.Type.date: 757 return JSON(get!BSONDate.toString()); 758 case BSON.Type.null_: 759 return JSON(null); 760 case BSON.Type.regex: 761 assert(false, "TODO"); 762 case BSON.Type.dbRef: 763 assert(false, "TODO"); 764 case BSON.Type.code: 765 return JSON(get!string()); 766 case BSON.Type.symbol: 767 return JSON(get!string()); 768 case BSON.Type.codeWScope: 769 assert(false, "TODO"); 770 case BSON.Type.int_: 771 return JSON(get!int()); 772 case BSON.Type.timestamp: 773 return JSON(get!BSONTimestamp().m_time); 774 case BSON.Type.long_: 775 return JSON(get!long()); 776 case BSON.Type.undefined: 777 return JSON(); 778 } 779 } 780 781 /** Returns a string representation of this BSON value in JSON format. 782 */ 783 string toString() const { 784 return toJSON().toString(); 785 } 786 787 import std.typecons : Nullable; 788 789 /** 790 Check whether the BSON object contains the given key. 791 */ 792 Nullable!BSON tryIndex(string key) const { 793 checkType(Type.object); 794 foreach (string idx, v; this.byKeyValue) 795 if (idx == key) 796 return Nullable!BSON(v); 797 return Nullable!BSON.init; 798 } 799 800 /** Allows accessing fields of a BSON object using `[]`. 801 802 Returns a null value if the specified field does not exist. 803 */ 804 inout(BSON) opIndex(string idx) inout { 805 foreach (string key, v; this.byKeyValue) 806 if (key == idx) 807 return v; 808 return BSON(null); 809 } 810 /// ditto 811 void opIndexAssign(T)(in T value, string idx) { 812 auto newcont = appender!bdata_t(); 813 checkType(Type.object); 814 auto d = m_data[4 .. $]; 815 while (d.length > 0) { 816 auto tp = cast(Type) d[0]; 817 if (tp == Type.end) 818 break; 819 d = d[1 .. $]; 820 auto key = skipCString(d); 821 auto val = BSON(tp, d); 822 d = d[val.data.length .. $]; 823 824 if (key != idx) { 825 // copy to new array 826 newcont.put(cast(ubyte) tp); 827 putCString(newcont, key); 828 newcont.put(val.data); 829 } 830 } 831 832 static if (is(T == BSON)) 833 alias bval = value; 834 else 835 auto bval = BSON(value); 836 837 newcont.put(cast(ubyte) bval.type); 838 putCString(newcont, idx); 839 newcont.put(bval.data); 840 841 auto newdata = appender!bdata_t(); 842 newdata.put(toBSONData(cast(uint)(newcont.data.length + 5))); 843 newdata.put(newcont.data); 844 newdata.put(cast(ubyte) 0); 845 m_data = newdata.data; 846 } 847 848 /// 849 unittest { 850 BSON value = BSON.emptyObject; 851 value["a"] = 1; 852 value["b"] = true; 853 value["c"] = "foo"; 854 assert(value["a"] == BSON(1)); 855 assert(value["b"] == BSON(true)); 856 assert(value["c"] == BSON("foo")); 857 } 858 859 /// 860 unittest { 861 auto srcUuid = UUID("00010203-0405-0607-0809-0a0b0c0d0e0f"); 862 863 BSON b = srcUuid; 864 auto u = b.get!UUID(); 865 866 assert(b.type == BSON.Type.binData); 867 assert(b.get!BSONBinData().type == BSONBinData.Type.uuid); 868 assert(u == srcUuid); 869 } 870 871 /** Allows index based access of a BSON array value. 872 873 Returns a null value if the index is out of bounds. 874 */ 875 inout(BSON) opIndex(size_t idx) inout { 876 foreach (size_t i, v; this.byIndexValue) 877 if (i == idx) 878 return v; 879 return BSON(null); 880 } 881 882 /// 883 unittest { 884 BSON[] entries; 885 entries ~= BSON(1); 886 entries ~= BSON(true); 887 entries ~= BSON("foo"); 888 889 BSON value = BSON(entries); 890 assert(value[0] == BSON(1)); 891 assert(value[1] == BSON(true)); 892 assert(value[2] == BSON("foo")); 893 } 894 895 /** Removes an entry from a BSON obect. 896 897 If the key doesn't exit, this function will be a no-op. 898 */ 899 void remove(string key) { 900 checkType(Type.object); 901 auto d = m_data[4 .. $]; 902 while (d.length > 0) { 903 size_t start_remainder = d.length; 904 auto tp = cast(Type) d[0]; 905 if (tp == Type.end) 906 break; 907 d = d[1 .. $]; 908 auto ekey = skipCString(d); 909 auto evalue = BSON(tp, d); 910 d = d[evalue.data.length .. $]; 911 912 if (ekey == key) { 913 m_data = m_data[0 .. $ - start_remainder] ~ d; 914 break; 915 } 916 } 917 } 918 919 unittest { 920 auto o = BSON.emptyObject; 921 o["a"] = BSON(1); 922 o["b"] = BSON(2); 923 o["c"] = BSON(3); 924 assert(o.length == 3); 925 o.remove("b"); 926 assert(o.length == 2); 927 assert(o["a"] == BSON(1)); 928 assert(o["c"] == BSON(3)); 929 o.remove("c"); 930 assert(o.length == 1); 931 assert(o["a"] == BSON(1)); 932 o.remove("c"); 933 assert(o.length == 1); 934 assert(o["a"] == BSON(1)); 935 o.remove("a"); 936 assert(o.length == 0); 937 } 938 939 /** 940 Allows foreach iterating over BSON objects and arrays. 941 */ 942 int opApply(scope int delegate(BSON obj) del) const @system { 943 foreach (value; byValue) 944 if (auto ret = del(value)) 945 return ret; 946 return 0; 947 } 948 /// ditto 949 int opApply(scope int delegate(size_t idx, BSON obj) del) const @system { 950 foreach (index, value; byIndexValue) 951 if (auto ret = del(index, value)) 952 return ret; 953 return 0; 954 } 955 /// ditto 956 int opApply(scope int delegate(string idx, BSON obj) del) const @system { 957 foreach (key, value; byKeyValue) 958 if (auto ret = del(key, value)) 959 return ret; 960 return 0; 961 } 962 963 /// Iterates over all values of an object or array. 964 auto byValue() const { 965 checkType(Type.array, Type.object); 966 return byKeyValueImpl().map!(t => t[1]); 967 } 968 /// Iterates over all index/value pairs of an array. 969 auto byIndexValue() const { 970 checkType(Type.array); 971 return byKeyValueImpl().map!(t => Tuple!(size_t, "key", BSON, "value")(t[0].to!size_t, t[1])); 972 } 973 /// Iterates over all key/value pairs of an object. 974 auto byKeyValue() const { 975 checkType(Type.object); 976 return byKeyValueImpl(); 977 } 978 979 private auto byKeyValueImpl() const { 980 checkType(Type.object, Type.array); 981 982 alias T = Tuple!(string, "key", BSON, "value"); 983 984 static struct Rng { 985 private { 986 immutable(ubyte)[] data; 987 string key; 988 BSON value; 989 } 990 991 @property bool empty() const { 992 return data.length == 0; 993 } 994 995 @property T front() { 996 return T(key, value); 997 } 998 999 @property Rng save() const { 1000 return this; 1001 } 1002 1003 void popFront() { 1004 auto tp = cast(Type) data[0]; 1005 data = data[1 .. $]; 1006 if (tp == Type.end) 1007 return; 1008 key = skipCString(data); 1009 value = BSON(tp, data); 1010 data = data[value.data.length .. $]; 1011 } 1012 } 1013 1014 auto ret = Rng(m_data[4 .. $]); 1015 ret.popFront(); 1016 return ret; 1017 } 1018 1019 /// 1020 bool opEquals(ref const BSON other) const { 1021 if (m_type != other.m_type) 1022 return false; 1023 if (m_type != Type.object) 1024 return m_data == other.m_data; 1025 1026 if (m_data == other.m_data) 1027 return true; 1028 // Similar objects can have a different key order, but they must have a same length 1029 if (m_data.length != other.m_data.length) 1030 return false; 1031 1032 foreach (k, ref v; this.byKeyValue) { 1033 if (other[k] != v) 1034 return false; 1035 } 1036 1037 return true; 1038 } 1039 /// ditto 1040 bool opEquals(const BSON other) const { 1041 if (m_type != other.m_type) 1042 return false; 1043 1044 return opEquals(other); 1045 } 1046 1047 private void checkType(in Type[] valid_types...) const { 1048 foreach (t; valid_types) 1049 if (m_type == t) 1050 return; 1051 throw new Exception("BSON value is type '" ~ to!string( 1052 m_type) ~ "', expected to be one of " ~ to!string(valid_types)); 1053 } 1054 } 1055 1056 /** 1057 Represents a BSON binary data value (BSON.Type.binData). 1058 */ 1059 struct BSONBinData { 1060 @safe: 1061 1062 enum Type : ubyte { 1063 generic = 0x00, 1064 function_ = 0x01, 1065 binaryOld = 0x02, 1066 uuid = 0x04, 1067 md5 = 0x05, 1068 userDefined = 0x80, 1069 1070 Generic = generic, /// Compatibility alias - will be deprecated soon 1071 Function = function_, /// Compatibility alias - will be deprecated soon 1072 BinaryOld = binaryOld, /// Compatibility alias - will be deprecated soon 1073 UUID = uuid, /// Compatibility alias - will be deprecated soon 1074 MD5 = md5, /// Compatibility alias - will be deprecated soon 1075 UserDefined = userDefined, /// Compatibility alias - will be deprecated soon 1076 } 1077 1078 private { 1079 Type m_type; 1080 bdata_t m_data; 1081 } 1082 1083 this(Type type, immutable(ubyte)[] data) { 1084 m_type = type; 1085 m_data = data; 1086 } 1087 1088 @property Type type() const { 1089 return m_type; 1090 } 1091 1092 @property bdata_t rawData() const { 1093 return m_data; 1094 } 1095 } 1096 1097 /** 1098 Represents a BSON object id (BSON.Type.binData). 1099 */ 1100 struct BSONObjectID { 1101 @safe: 1102 1103 private { 1104 ubyte[12] m_bytes; 1105 static immutable uint MACHINE_ID; 1106 static immutable int ms_pid; 1107 static uint ms_inc = 0; 1108 } 1109 1110 shared static this() { 1111 import std.process; 1112 import std.random; 1113 1114 MACHINE_ID = uniform(0, 0xffffff); 1115 ms_pid = thisProcessID; 1116 } 1117 1118 static this() { 1119 import std.random; 1120 1121 ms_inc = uniform(0, 0xffffff); 1122 } 1123 1124 /** Constructs a new object ID from the given raw byte array. 1125 */ 1126 this(in ubyte[] bytes) { 1127 assert(bytes.length == 12); 1128 m_bytes[] = bytes[]; 1129 } 1130 1131 /** Creates an on object ID from a string in standard hexa-decimal form. 1132 */ 1133 static BSONObjectID fromString(string str) { 1134 import std.conv : ConvException; 1135 1136 static const lengthex = new ConvException("BSON Object ID string must be 24 characters."); 1137 static const charex = new ConvException("Not a valid hex string."); 1138 1139 if (str.length != 24) 1140 throw lengthex; 1141 BSONObjectID ret = void; 1142 uint b = 0; 1143 foreach (i, ch; str) { 1144 ubyte n; 1145 if (ch >= '0' && ch <= '9') 1146 n = cast(ubyte)(ch - '0'); 1147 else if (ch >= 'a' && ch <= 'f') 1148 n = cast(ubyte)(ch - 'a' + 10); 1149 else if (ch >= 'A' && ch <= 'F') 1150 n = cast(ubyte)(ch - 'F' + 10); 1151 else 1152 throw charex; 1153 b <<= 4; 1154 b += n; 1155 if (i % 8 == 7) { 1156 auto j = i / 8; 1157 ret.m_bytes[j * 4 .. (j + 1) * 4] = toBigEndianData(b)[]; 1158 b = 0; 1159 } 1160 } 1161 return ret; 1162 } 1163 /// ditto 1164 alias fromHexString = fromString; 1165 1166 /** Generates a unique object ID. 1167 * 1168 * By default it will use `Clock.currTime(UTC())` as the timestamp 1169 * which guarantees that `BSONObjectID`s are chronologically 1170 * sorted. 1171 */ 1172 static BSONObjectID generate(in SysTime time = Clock.currTime(UTC())) { 1173 import std.datetime; 1174 1175 BSONObjectID ret = void; 1176 ret.m_bytes[0 .. 4] = toBigEndianData(cast(uint) time.toUnixTime())[]; 1177 ret.m_bytes[4 .. 7] = toBSONData(MACHINE_ID)[0 .. 3]; 1178 ret.m_bytes[7 .. 9] = toBSONData(cast(ushort) ms_pid)[]; 1179 ret.m_bytes[9 .. 12] = toBigEndianData(ms_inc++)[1 .. 4]; 1180 return ret; 1181 } 1182 1183 /** Creates a pseudo object ID that matches the given date. 1184 1185 This kind of ID can be useful to query a database for items in a certain 1186 date interval using their ID. This works using the property of standard BSON 1187 object IDs that they store their creation date as part of the ID. Note that 1188 this date part is only 32-bit wide and is limited to the same timespan as a 1189 32-bit Unix timestamp. 1190 */ 1191 static BSONObjectID createDateID(in SysTime time) { 1192 BSONObjectID ret; 1193 ret.m_bytes[0 .. 4] = toBigEndianData(cast(uint) time.toUnixTime())[]; 1194 return ret; 1195 } 1196 1197 /** Returns true for any non-zero ID. 1198 */ 1199 @property bool valid() const { 1200 foreach (b; m_bytes) 1201 if (b != 0) 1202 return true; 1203 return false; 1204 } 1205 1206 /** Extracts the time/date portion of the object ID. 1207 1208 For IDs created using the standard generation algorithm or using createDateID 1209 this will return the associated time stamp. 1210 */ 1211 @property SysTime timeStamp() const { 1212 ubyte[4] tm = m_bytes[0 .. 4]; 1213 return SysTime(unixTimeToStdTime(bigEndianToNative!uint(tm))); 1214 } 1215 1216 /** Allows for relational comparison of different IDs. 1217 */ 1218 int opCmp(ref const BSONObjectID other) const { 1219 import core.stdc.string; 1220 1221 return () @trusted { 1222 return memcmp(m_bytes.ptr, other.m_bytes.ptr, m_bytes.length); 1223 }(); 1224 } 1225 1226 /** Converts the ID to its standard hexa-decimal string representation. 1227 */ 1228 string toString() const pure { 1229 enum hexdigits = "0123456789abcdef"; 1230 auto ret = new char[24]; 1231 foreach (i, b; m_bytes) { 1232 ret[i * 2 + 0] = hexdigits[(b >> 4) & 0x0F]; 1233 ret[i * 2 + 1] = hexdigits[b & 0x0F]; 1234 } 1235 return ret; 1236 } 1237 1238 inout(ubyte)[] opCast() inout { 1239 return m_bytes; 1240 } 1241 } 1242 1243 unittest { 1244 auto t0 = SysTime(Clock.currTime(UTC()).toUnixTime.unixTimeToStdTime); 1245 auto id = BSONObjectID.generate(); 1246 auto t1 = SysTime(Clock.currTime(UTC()).toUnixTime.unixTimeToStdTime); 1247 assert(t0 <= id.timeStamp); 1248 assert(id.timeStamp <= t1); 1249 1250 id = BSONObjectID.generate(t0); 1251 assert(id.timeStamp == t0); 1252 1253 id = BSONObjectID.generate(t1); 1254 assert(id.timeStamp == t1); 1255 1256 immutable dt = DateTime(2014, 07, 31, 19, 14, 55); 1257 id = BSONObjectID.generate(SysTime(dt, UTC())); 1258 assert(id.timeStamp == SysTime(dt, UTC())); 1259 } 1260 1261 unittest { 1262 auto b = BSON(true); 1263 assert(b.opt!bool(false) == true); 1264 assert(b.opt!int(12) == 12); 1265 assert(b.opt!(BSON[])(null).length == 0); 1266 1267 const c = b; 1268 assert(c.opt!bool(false) == true); 1269 assert(c.opt!int(12) == 12); 1270 assert(c.opt!(BSON[])(null).length == 0); 1271 } 1272 1273 /** 1274 Represents a BSON date value (`BSON.Type.date`). 1275 1276 BSON date values are stored in UNIX time format, counting the number of 1277 milliseconds from 1970/01/01. 1278 */ 1279 struct BSONDate { 1280 @safe: 1281 1282 private long m_time; // milliseconds since UTC unix epoch 1283 1284 /** Constructs a BSONDate from the given date value. 1285 1286 The time-zone independent Date and DateTime types are assumed to be in 1287 the local time zone and converted to UTC if tz is left to null. 1288 */ 1289 this(in Date date, immutable TimeZone tz = null) { 1290 this(SysTime(date, tz)); 1291 } 1292 /// ditto 1293 this(in DateTime date, immutable TimeZone tz = null) { 1294 this(SysTime(date, tz)); 1295 } 1296 /// ditto 1297 this(in SysTime date) { 1298 this(fromStdTime(date.stdTime()).m_time); 1299 } 1300 1301 /** Constructs a BSONDate from the given UNIX time. 1302 1303 unix_time needs to be given in milliseconds from 1970/01/01. This is 1304 the native storage format for BSONDate. 1305 */ 1306 this(long unix_time) { 1307 m_time = unix_time; 1308 } 1309 1310 /** Constructs a BSONDate from the given date/time string in ISO extended format. 1311 */ 1312 static BSONDate fromString(string iso_ext_string) { 1313 return BSONDate(SysTime.fromISOExtString(iso_ext_string)); 1314 } 1315 1316 /** Constructs a BSONDate from the given date/time in standard time as defined in `std.datetime`. 1317 */ 1318 static BSONDate fromStdTime(long std_time) { 1319 enum zero = unixTimeToStdTime(0); 1320 return BSONDate((std_time - zero) / 10_000L); 1321 } 1322 1323 /** The raw unix time value. 1324 1325 This is the native storage/transfer format of a BSONDate. 1326 */ 1327 @property long value() const { 1328 return m_time; 1329 } 1330 /// ditto 1331 @property void value(long v) { 1332 m_time = v; 1333 } 1334 1335 /** Returns the date formatted as ISO extended format. 1336 */ 1337 string toString() const { 1338 return toSysTime().toISOExtString(); 1339 } 1340 1341 /* Converts to a SysTime using UTC timezone. 1342 */ 1343 SysTime toSysTime() const { 1344 return toSysTime(UTC()); 1345 } 1346 1347 /* Converts to a SysTime with a given timezone. 1348 */ 1349 SysTime toSysTime(immutable TimeZone tz) const { 1350 auto zero = unixTimeToStdTime(0); 1351 return SysTime(zero + m_time * 10_000L, tz); 1352 } 1353 1354 /** Allows relational and equality comparisons. 1355 */ 1356 bool opEquals(ref const BSONDate other) const { 1357 return m_time == other.m_time; 1358 } 1359 /// ditto 1360 int opCmp(ref const BSONDate other) const { 1361 if (m_time == other.m_time) 1362 return 0; 1363 if (m_time < other.m_time) 1364 return -1; 1365 else 1366 return 1; 1367 } 1368 } 1369 1370 /** 1371 Represents a BSON timestamp value `(BSON.Type.timestamp)`. 1372 */ 1373 struct BSONTimestamp { 1374 @safe: 1375 1376 private long m_time; 1377 1378 this(long time) { 1379 m_time = time; 1380 } 1381 } 1382 1383 /** 1384 Represents a BSON regular expression value `(BSON.Type.regex)`. 1385 */ 1386 struct BSONRegex { 1387 @safe: 1388 1389 private { 1390 string m_expr; 1391 string m_options; 1392 } 1393 1394 this(string expr, string options) { 1395 m_expr = expr; 1396 m_options = options; 1397 } 1398 1399 @property string expression() const { 1400 return m_expr; 1401 } 1402 1403 @property string options() const { 1404 return m_options; 1405 } 1406 } 1407 1408 /** 1409 Serializes the given value to BSON. 1410 1411 The following types of values are supported: 1412 1413 $(DL 1414 $(DT `BSON`) $(DD Used as-is) 1415 $(DT `JSON`) $(DD Converted to BSON) 1416 $(DT `BSONBinData`) $(DD Converted to `BSON.Type.binData`) 1417 $(DT `BSONObjectID`) $(DD Converted to `BSON.Type.objectID`) 1418 $(DT `BSONDate`) $(DD Converted to `BSON.Type.date`) 1419 $(DT `BSONTimestamp`) $(DD Converted to `BSON.Type.timestamp`) 1420 $(DT `BSONRegex`) $(DD Converted to `BSON.Type.regex`) 1421 $(DT `null`) $(DD Converted to `BSON.Type.null_`) 1422 $(DT `bool`) $(DD Converted to `BSON.Type.bool_`) 1423 $(DT `float`, `double`) $(DD Converted to `BSON.Type.double_`) 1424 $(DT `short`, `ushort`, `int`, `uint`, `long`, `ulong`) $(DD Converted to `BSON.Type.long_`) 1425 $(DT `string`) $(DD Converted to `BSON.Type.string`) 1426 $(DT `ubyte[]`) $(DD Converted to `BSON.Type.binData`) 1427 $(DT `T[]`) $(DD Converted to `BSON.Type.array`) 1428 $(DT `T[string]`) $(DD Converted to `BSON.Type.object`) 1429 $(DT `struct`) $(DD Converted to `BSON.Type.object`) 1430 $(DT `class`) $(DD Converted to `BSON.Type.object` or `BSON.Type.null_`) 1431 ) 1432 1433 All entries of an array or an associative array, as well as all R/W properties and 1434 all fields of a struct/class are recursively serialized using the same rules. 1435 1436 Fields ending with an underscore will have the last underscore stripped in the 1437 serialized output. This makes it possible to use fields with D keywords as their name 1438 by simply appending an underscore. 1439 1440 The following methods can be used to customize the serialization of structs/classes: 1441 1442 --- 1443 BSON toBSON() const; 1444 static T fromBSON(BSON src); 1445 1446 JSON toJSON() const; 1447 static T fromJSON(JSON src); 1448 1449 string toString() const; 1450 static T fromString(string src); 1451 --- 1452 1453 The methods will have to be defined in pairs. The first pair that is implemented by 1454 the type will be used for serialization (i.e. `toBSON` overrides `toJSON`). 1455 1456 See_Also: `deserializeBSON` 1457 */ 1458 import dutils.data.utils.serialization; 1459 1460 BSON serializeToBSON(T)(auto ref T value, ubyte[] buffer = null) { 1461 return serialize!BSONSerializer(value, buffer); 1462 } 1463 1464 template deserializeBSON(T) { 1465 /** 1466 Deserializes a BSON value into the destination variable. 1467 1468 The same types as for `serializeToBSON()` are supported and handled inversely. 1469 1470 See_Also: `serializeToBSON` 1471 */ 1472 void deserializeBSON(ref T dst, BSON src) { 1473 dst = deserializeBSON!T(src); 1474 } 1475 /// ditto 1476 T deserializeBSON(BSON src) { 1477 return deserialize!(BSONSerializer, T)(src); 1478 } 1479 } 1480 1481 unittest { 1482 import std.stdio; 1483 1484 enum Foo : string { 1485 k = "test" 1486 } 1487 1488 enum Boo : int { 1489 l = 5 1490 } 1491 1492 static struct S { 1493 float a; 1494 double b; 1495 bool c; 1496 int d; 1497 string e; 1498 byte f; 1499 ubyte g; 1500 long h; 1501 ulong i; 1502 float[] j; 1503 Foo k; 1504 Boo l; 1505 } 1506 1507 immutable S t = { 1508 1.5, -3.0, true, int.min, "Test", -128, 255, long.min, ulong.max, [ 1509 1.1, 1.2, 1.3 1510 ], Foo.k, Boo.l,}; 1511 S u; 1512 deserializeBSON(u, serializeToBSON(t)); 1513 assert(t.a == u.a); 1514 assert(t.b == u.b); 1515 assert(t.c == u.c); 1516 assert(t.d == u.d); 1517 assert(t.e == u.e); 1518 assert(t.f == u.f); 1519 assert(t.g == u.g); 1520 assert(t.h == u.h); 1521 assert(t.i == u.i); 1522 assert(t.j == u.j); 1523 assert(t.k == u.k); 1524 assert(t.l == u.l); 1525 } 1526 1527 unittest { 1528 assert(uint.max == serializeToBSON(uint.max).deserializeBSON!uint); 1529 assert(ulong.max == serializeToBSON(ulong.max).deserializeBSON!ulong); 1530 } 1531 1532 unittest { 1533 assert(deserializeBSON!SysTime(serializeToBSON(SysTime(0))) == SysTime(0)); 1534 assert(deserializeBSON!SysTime(serializeToBSON(SysTime(0, UTC()))) == SysTime(0, UTC())); 1535 assert(deserializeBSON!Date(serializeToBSON(Date.init)) == Date.init); 1536 assert(deserializeBSON!Date(serializeToBSON(Date(2001, 1, 1))) == Date(2001, 1, 1)); 1537 } 1538 1539 @safe unittest { 1540 static struct A { 1541 int value; 1542 static A fromJSON(JSON val) @safe { 1543 return A(val.get!int); 1544 } 1545 1546 JSON toJSON() const @safe { 1547 return JSON(value); 1548 } 1549 1550 BSON toBSON() { 1551 return BSON(); 1552 } 1553 } 1554 1555 static assert(!isStringSerializable!A && isJSONSerializable!A && !isBSONSerializable!A); 1556 static assert(!isStringSerializable!(const(A)) 1557 && isJSONSerializable!(const(A)) && !isBSONSerializable!(const(A))); 1558 // assert(serializeToBSON(const A(123)) == BSON(123)); 1559 // assert(serializeToBSON(A(123)) == BSON(123)); 1560 1561 static struct B { 1562 int value; 1563 static B fromBSON(BSON val) @safe { 1564 return B(val.get!int); 1565 } 1566 1567 BSON toBSON() const @safe { 1568 return BSON(value); 1569 } 1570 1571 JSON toJSON() { 1572 return JSON(); 1573 } 1574 } 1575 1576 static assert(!isStringSerializable!B && !isJSONSerializable!B && isBSONSerializable!B); 1577 static assert(!isStringSerializable!(const(B)) 1578 && !isJSONSerializable!(const(B)) && isBSONSerializable!(const(B))); 1579 assert(serializeToBSON(const B(123)) == BSON(123)); 1580 assert(serializeToBSON(B(123)) == BSON(123)); 1581 1582 static struct C { 1583 int value; 1584 static C fromString(string val) @safe { 1585 return C(val.to!int); 1586 } 1587 1588 string toString() const @safe { 1589 return value.to!string; 1590 } 1591 1592 JSON toJSON() { 1593 return JSON(); 1594 } 1595 } 1596 1597 static assert(isStringSerializable!C && !isJSONSerializable!C && !isBSONSerializable!C); 1598 static assert(isStringSerializable!(const(C)) 1599 && !isJSONSerializable!(const(C)) && !isBSONSerializable!(const(C))); 1600 assert(serializeToBSON(const C(123)) == BSON("123")); 1601 assert(serializeToBSON(C(123)) == BSON("123")); 1602 1603 static struct D { 1604 int value; 1605 string toString() const { 1606 return ""; 1607 } 1608 } 1609 1610 static assert(!isStringSerializable!D && !isJSONSerializable!D && !isBSONSerializable!D); 1611 static assert(!isStringSerializable!(const(D)) 1612 && !isJSONSerializable!(const(D)) && !isBSONSerializable!(const(D))); 1613 assert(serializeToBSON(const D(123)) == serializeToBSON(["value": 123])); 1614 assert(serializeToBSON(D(123)) == serializeToBSON(["value": 123])); 1615 1616 // test if const(class) is serializable 1617 static class E { 1618 int value; 1619 this(int v) @safe { 1620 value = v; 1621 } 1622 1623 static E fromBSON(BSON val) @safe { 1624 return new E(val.get!int); 1625 } 1626 1627 BSON toBSON() const @safe { 1628 return BSON(value); 1629 } 1630 1631 JSON toJSON() { 1632 return JSON(); 1633 } 1634 } 1635 1636 static assert(!isStringSerializable!E && !isJSONSerializable!E && isBSONSerializable!E); 1637 static assert(!isStringSerializable!(const(E)) 1638 && !isJSONSerializable!(const(E)) && isBSONSerializable!(const(E))); 1639 assert(serializeToBSON(new const E(123)) == BSON(123)); 1640 assert(serializeToBSON(new E(123)) == BSON(123)); 1641 } 1642 1643 @safe unittest { 1644 static struct E { 1645 ubyte[4] bytes; 1646 ubyte[] more; 1647 } 1648 1649 auto e = E([1, 2, 3, 4], [5, 6]); 1650 auto eb = serializeToBSON(e); 1651 assert(eb["bytes"].type == BSON.Type.binData); 1652 assert(eb["more"].type == BSON.Type.binData); 1653 assert(e == deserializeBSON!E(eb)); 1654 } 1655 1656 @safe unittest { 1657 static class C { 1658 @safe: 1659 int a; 1660 private int _b; 1661 @property int b() const { 1662 return _b; 1663 } 1664 1665 @property void b(int v) { 1666 _b = v; 1667 } 1668 1669 @property int test() const @safe { 1670 return 10; 1671 } 1672 1673 void test2() { 1674 } 1675 } 1676 1677 C c = new C; 1678 c.a = 1; 1679 c.b = 2; 1680 1681 C d; 1682 deserializeBSON(d, serializeToBSON(c)); 1683 assert(c.a == d.a); 1684 assert(c.b == d.b); 1685 1686 const(C) e = c; // serialize const class instances (issue #653) 1687 deserializeBSON(d, serializeToBSON(e)); 1688 assert(e.a == d.a); 1689 assert(e.b == d.b); 1690 } 1691 1692 unittest { 1693 static struct C { 1694 @safe: 1695 int value; 1696 static C fromString(string val) { 1697 return C(val.to!int); 1698 } 1699 1700 string toString() const { 1701 return value.to!string; 1702 } 1703 } 1704 1705 enum Color { 1706 Red, 1707 Green, 1708 Blue 1709 } 1710 1711 { 1712 static class T { 1713 @safe: 1714 string[Color] enumIndexedMap; 1715 string[C] stringableIndexedMap; 1716 this() { 1717 enumIndexedMap = [Color.Red: "magenta", Color.Blue: "deep blue"]; 1718 stringableIndexedMap = [C(42): "forty-two"]; 1719 } 1720 } 1721 1722 T original = new T; 1723 original.enumIndexedMap[Color.Green] = "olive"; 1724 T other; 1725 deserializeBSON(other, serializeToBSON(original)); 1726 assert(serializeToBSON(other) == serializeToBSON(original)); 1727 } 1728 { 1729 static struct S { 1730 string[Color] enumIndexedMap; 1731 string[C] stringableIndexedMap; 1732 } 1733 1734 S original; 1735 original.enumIndexedMap = [Color.Red: "magenta", Color.Blue: "deep blue"]; 1736 original.enumIndexedMap[Color.Green] = "olive"; 1737 original.stringableIndexedMap = [C(42): "forty-two"]; 1738 S other; 1739 deserializeBSON(other, serializeToBSON(original)); 1740 assert(serializeToBSON(other) == serializeToBSON(original)); 1741 } 1742 } 1743 1744 unittest { 1745 ubyte[] data = [1, 2, 3]; 1746 auto BSON = serializeToBSON(data); 1747 assert(BSON.type == BSON.Type.binData); 1748 assert(deserializeBSON!(ubyte[])(BSON) == data); 1749 } 1750 1751 unittest { // issue #709 1752 ulong[] data = [2354877787627192443, 1, 2354877787627192442]; 1753 auto BSON = BSON.fromJSON(serializeToBSON(data).toJSON); 1754 assert(deserializeBSON!(ulong[])(BSON) == data); 1755 } 1756 1757 unittest { // issue #709 1758 uint[] data = [1, 2, 3, 4]; 1759 auto BSON = BSON.fromJSON(serializeToBSON(data).toJSON); 1760 // assert(deserializeBSON!(uint[])(BSON) == data); 1761 assert(deserializeBSON!(ulong[])(BSON).equal(data)); 1762 } 1763 1764 unittest { 1765 import std.typecons; 1766 1767 Nullable!bool x; 1768 auto BSON = serializeToBSON(x); 1769 assert(BSON.type == BSON.Type.null_); 1770 deserializeBSON(x, BSON); 1771 assert(x.isNull); 1772 x = true; 1773 BSON = serializeToBSON(x); 1774 assert(BSON.type == BSON.Type.bool_ && BSON.get!bool == true); 1775 deserializeBSON(x, BSON); 1776 assert(x == true); 1777 } 1778 1779 unittest { // issue #793 1780 char[] test = "test".dup; 1781 auto BSON = serializeToBSON(test); 1782 //assert(BSON.type == BSON.Type.string); 1783 //assert(BSON.get!string == "test"); 1784 assert(BSON.type == BSON.Type.array); 1785 assert(BSON[0].type == BSON.Type..string && BSON[0].get!string == "t"); 1786 } 1787 1788 @safe unittest { // issue #2212 1789 auto bsonRegex = BSON(BSONRegex(".*", "i")); 1790 auto parsedRegex = bsonRegex.get!BSONRegex; 1791 assert(bsonRegex.type == BSON.Type.regex); 1792 assert(parsedRegex.expression == ".*"); 1793 assert(parsedRegex.options == "i"); 1794 } 1795 1796 unittest { 1797 UUID uuid = UUID("35399104-fbc9-4c08-bbaf-65a5efe6f5f2"); 1798 1799 auto bson = BSON(uuid); 1800 assert(bson.get!UUID == uuid); 1801 assert(bson.deserializeBSON!UUID == uuid); 1802 1803 bson = BSON([BSON(uuid)]); 1804 assert(bson.deserializeBSON!(UUID[]) == [uuid]); 1805 1806 bson = [uuid].serializeToBSON(); 1807 assert(bson.deserializeBSON!(UUID[]) == [uuid]); 1808 } 1809 1810 /** 1811 Serializes to an in-memory BSON representation. 1812 1813 See_Also: `vibe.data.serialization.serialize`, `vibe.data.serialization.deserialize`, `serializeToBSON`, `deserializeBSON` 1814 */ 1815 struct BSONSerializer { 1816 import dutils.data.utils.array : AllocAppender; 1817 1818 private { 1819 AllocAppender!(ubyte[]) m_dst; 1820 size_t[] m_compositeStack; 1821 BSON.Type m_type = BSON.Type.null_; 1822 BSON m_inputData; 1823 string m_entryName; 1824 size_t m_entryIndex = size_t.max; 1825 } 1826 1827 this(BSON input) @safe { 1828 m_inputData = input; 1829 } 1830 1831 this(ubyte[] buffer) @safe { 1832 import dutils.data.utils.utilallocator; 1833 1834 m_dst = () @trusted { 1835 return AllocAppender!(ubyte[])(vibeThreadAllocator(), buffer); 1836 }(); 1837 } 1838 1839 @disable this(this); 1840 1841 template isSupportedValueType(T) { 1842 enum isSupportedValueType = is(typeof(getBSONTypeID(T.init))); 1843 } 1844 1845 // 1846 // serialization 1847 // 1848 BSON getSerializedResult() @safe { 1849 auto ret = BSON(m_type, () @trusted { return cast(immutable) m_dst.data; }()); 1850 () @trusted { m_dst.reset(); }(); 1851 m_type = BSON.Type.null_; 1852 return ret; 1853 } 1854 1855 void beginWriteDictionary(Traits)() { 1856 writeCompositeEntryHeader(BSON.Type.object); 1857 m_compositeStack ~= m_dst.data.length; 1858 m_dst.put(toBSONData(cast(int) 0)); 1859 } 1860 1861 void endWriteDictionary(Traits)() { 1862 m_dst.put(BSON.Type.end); 1863 auto sh = m_compositeStack[$ - 1]; 1864 m_compositeStack.length--; 1865 m_dst.data[sh .. sh + 4] = toBSONData(cast(uint)(m_dst.data.length - sh))[]; 1866 } 1867 1868 void beginWriteDictionaryEntry(Traits)(string name) { 1869 m_entryName = name; 1870 } 1871 1872 void endWriteDictionaryEntry(Traits)(string name) { 1873 } 1874 1875 void beginWriteArray(Traits)(size_t) { 1876 writeCompositeEntryHeader(BSON.Type.array); 1877 m_compositeStack ~= m_dst.data.length; 1878 m_dst.put(toBSONData(cast(int) 0)); 1879 } 1880 1881 void endWriteArray(Traits)() { 1882 endWriteDictionary!Traits(); 1883 } 1884 1885 void beginWriteArrayEntry(Traits)(size_t idx) { 1886 m_entryIndex = idx; 1887 } 1888 1889 void endWriteArrayEntry(Traits)(size_t idx) { 1890 } 1891 1892 void writeValue(Traits, T)(auto ref T value) { 1893 writeValueH!(T, true)(value); 1894 } 1895 1896 private void writeValueH(T, bool write_header)(auto ref T value) { 1897 alias UT = Unqual!T; 1898 static if (write_header) 1899 writeCompositeEntryHeader(getBSONTypeID(value)); 1900 1901 static if (is(UT == BSON)) { 1902 m_dst.put(value.data); 1903 } else static if (is(UT == JSON)) { 1904 m_dst.put(BSON(value).data); 1905 } // FIXME: use .writeBSONValue 1906 else static if (is(UT == typeof(null))) { 1907 } else static if (is(UT == string)) { 1908 m_dst.put(toBSONData(cast(uint) value.length + 1)); 1909 m_dst.putCString(value); 1910 } else static if (is(UT == BSONBinData)) { 1911 m_dst.put(toBSONData(cast(int) value.rawData.length)); 1912 m_dst.put(value.type); 1913 m_dst.put(value.rawData); 1914 } else static if (is(UT == BSONObjectID)) { 1915 m_dst.put(value.m_bytes[]); 1916 } else static if (is(UT == BSONDate)) { 1917 m_dst.put(toBSONData(value.m_time)); 1918 } else static if (is(UT == SysTime)) { 1919 m_dst.put(toBSONData(BSONDate(value).m_time)); 1920 } else static if (is(UT == BSONRegex)) { 1921 m_dst.putCString(value.expression); 1922 m_dst.putCString(value.options); 1923 } else static if (is(UT == BSONTimestamp)) { 1924 m_dst.put(toBSONData(value.m_time)); 1925 } else static if (is(UT == bool)) { 1926 m_dst.put(cast(ubyte)(value ? 0x01 : 0x00)); 1927 } else static if (is(UT : int) && isIntegral!UT) { 1928 m_dst.put(toBSONData(cast(int) value)); 1929 } else static if (is(UT : long) && isIntegral!UT) { 1930 m_dst.put(toBSONData(value)); 1931 } else static if (is(UT : double) && isFloatingPoint!UT) { 1932 m_dst.put(toBSONData(cast(double) value)); 1933 } else static if (is(UT == UUID)) { 1934 m_dst.put(BSON(value).data); 1935 } else static if (isBSONSerializable!UT) { 1936 static if (!__traits(compiles, ()@safe { return value.toBSON(); }())) 1937 pragma(msg, 1938 "Non-@safe toBSON/fromBSON methods are deprecated - annotate " 1939 ~ T.stringof ~ ".toBSON() with @safe."); 1940 m_dst.put(() @trusted { return value.toBSON(); }().data); 1941 } else static if (isJSONSerializable!UT) { 1942 static if (!__traits(compiles, ()@safe { return value.toJSON(); }())) 1943 pragma(msg, 1944 "Non-@safe toJSON/fromJSON methods are deprecated - annotate " 1945 ~ UT.stringof ~ ".toJSON() with @safe."); 1946 m_dst.put(BSON(() @trusted { return value.toJSON(); }()).data); 1947 } else static if (is(UT : const(ubyte)[])) { 1948 writeValueH!(BSONBinData, false)(BSONBinData(BSONBinData.Type.generic, value.idup)); 1949 } else 1950 static assert(false, "Unsupported type: " ~ UT.stringof); 1951 } 1952 1953 private void writeCompositeEntryHeader(BSON.Type tp) @safe { 1954 if (!m_compositeStack.length) { 1955 assert(m_type == BSON.Type.null_, "Overwriting root item."); 1956 m_type = tp; 1957 } 1958 1959 if (m_entryName !is null) { 1960 m_dst.put(tp); 1961 m_dst.putCString(m_entryName); 1962 m_entryName = null; 1963 } else if (m_entryIndex != size_t.max) { 1964 import std.format; 1965 1966 m_dst.put(tp); 1967 static struct Wrapper { 1968 @trusted: 1969 AllocAppender!(ubyte[])* app; 1970 void put(char ch) { 1971 (*app).put(ch); 1972 } 1973 1974 void put(in char[] str) { 1975 (*app).put(cast(const(ubyte)[]) str); 1976 } 1977 } 1978 1979 auto wr = Wrapper(&m_dst); 1980 wr.formattedWrite("%d\0", m_entryIndex); 1981 m_entryIndex = size_t.max; 1982 } 1983 } 1984 1985 // 1986 // deserialization 1987 // 1988 void readDictionary(Traits)(scope void delegate(string) @safe entry_callback) { 1989 enforce(m_inputData.type == BSON.Type.object, 1990 "Expected object instead of " ~ m_inputData.type.to!string()); 1991 auto old = m_inputData; 1992 foreach (string name, value; old.byKeyValue) { 1993 m_inputData = value; 1994 entry_callback(name); 1995 } 1996 m_inputData = old; 1997 } 1998 1999 void beginReadDictionaryEntry(Traits)(string name) { 2000 } 2001 2002 void endReadDictionaryEntry(Traits)(string name) { 2003 } 2004 2005 void readArray(Traits)(scope void delegate(size_t) @safe size_callback, 2006 scope void delegate() @safe entry_callback) { 2007 enforce(m_inputData.type == BSON.Type.array, 2008 "Expected array instead of " ~ m_inputData.type.to!string()); 2009 auto old = m_inputData; 2010 foreach (value; old.byValue) { 2011 m_inputData = value; 2012 entry_callback(); 2013 } 2014 m_inputData = old; 2015 } 2016 2017 void beginReadArrayEntry(Traits)(size_t index) { 2018 } 2019 2020 void endReadArrayEntry(Traits)(size_t index) { 2021 } 2022 2023 T readValue(Traits, T)() { 2024 static if (is(T == BSON)) 2025 return m_inputData; 2026 else static if (is(T == JSON)) 2027 return m_inputData.toJSON(); 2028 else static if (is(T == bool)) 2029 return m_inputData.get!bool(); 2030 else static if (is(T == uint)) 2031 return cast(T) m_inputData.get!int(); 2032 else static if (is(T : int)) { 2033 if (m_inputData.type == BSON.Type.long_) { 2034 enforce((m_inputData.get!long() >= int.min) && (m_inputData.get!long() <= int.max), 2035 "Long out of range while attempting to deserialize to int: " ~ m_inputData.get!long 2036 .to!string); 2037 return cast(T) m_inputData.get!long(); 2038 } else 2039 return m_inputData.get!int().to!T; 2040 } else static if (is(T : long)) { 2041 if (m_inputData.type == BSON.Type.int_) 2042 return cast(T) m_inputData.get!int(); 2043 else 2044 return cast(T) m_inputData.get!long(); 2045 } else static if (is(T : double)) 2046 return cast(T) m_inputData.get!double(); 2047 else static if (is(T == SysTime)) { 2048 // support legacy behavior to serialize as string 2049 if (m_inputData.type == BSON.Type..string) 2050 return SysTime.fromISOExtString(m_inputData.get!string); 2051 else 2052 return m_inputData.get!BSONDate().toSysTime(); 2053 } else static if (isBSONSerializable!T) { 2054 static if (!__traits(compiles, ()@safe { return T.fromBSON(BSON.init); }())) 2055 pragma(msg, 2056 "Non-@safe toBSON/fromBSON methods are deprecated - annotate " 2057 ~ T.stringof ~ ".fromBSON() with @safe."); 2058 auto bval = readValue!(Traits, BSON); 2059 return () @trusted { return T.fromBSON(bval); }(); 2060 } else static if (isJSONSerializable!T) { 2061 static if (!__traits(compiles, ()@safe { return T.fromJSON(JSON.init); }())) 2062 pragma(msg, 2063 "Non-@safe toJSON/fromJSON methods are deprecated - annotate " 2064 ~ T.stringof ~ ".fromJSON() with @safe."); 2065 auto jval = readValue!(Traits, BSON).toJSON(); 2066 return () @trusted { return T.fromJSON(jval); }(); 2067 } else static if (is(T : const(ubyte)[])) { 2068 auto ret = m_inputData.get!BSONBinData.rawData; 2069 static if (isStaticArray!T) 2070 return cast(T) ret[0 .. T.length]; 2071 else static if (is(T : immutable(char)[])) 2072 return ret; 2073 else 2074 return cast(T) ret.dup; 2075 } else 2076 return m_inputData.get!T(); 2077 } 2078 2079 bool tryReadNull(Traits)() { 2080 if (m_inputData.type == BSON.Type.null_) 2081 return true; 2082 return false; 2083 } 2084 2085 private static BSON.Type getBSONTypeID(T, bool accept_ao = false)(auto ref T value) @safe { 2086 alias UT = Unqual!T; 2087 BSON.Type tp; 2088 static if (is(T == BSON)) 2089 tp = value.type; 2090 else static if (is(UT == JSON)) 2091 tp = JSONTypeToBSONType(value.type); 2092 else static if (is(UT == typeof(null))) 2093 tp = BSON.Type.null_; 2094 else static if (is(UT == string)) 2095 tp = BSON.Type..string; 2096 else static if (is(UT == BSONBinData)) 2097 tp = BSON.Type.binData; 2098 else static if (is(UT == BSONObjectID)) 2099 tp = BSON.Type.objectID; 2100 else static if (is(UT == BSONDate)) 2101 tp = BSON.Type.date; 2102 else static if (is(UT == SysTime)) 2103 tp = BSON.Type.date; 2104 else static if (is(UT == BSONRegex)) 2105 tp = BSON.Type.regex; 2106 else static if (is(UT == BSONTimestamp)) 2107 tp = BSON.Type.timestamp; 2108 else static if (is(UT == bool)) 2109 tp = BSON.Type.bool_; 2110 else static if (isIntegral!UT && is(UT : int)) 2111 tp = BSON.Type.int_; 2112 else static if (isIntegral!UT && is(UT : long)) 2113 tp = BSON.Type.long_; 2114 else static if (isFloatingPoint!UT && is(UT : double)) 2115 tp = BSON.Type.double_; 2116 else static if (isBSONSerializable!UT) 2117 tp = value.toBSON().type; // FIXME: this is highly inefficient 2118 else static if (isJSONSerializable!UT) 2119 tp = JSONTypeToBSONType(value.toJSON().type); // FIXME: this is highly inefficient 2120 else static if (is(UT == UUID)) 2121 tp = BSON.Type.binData; 2122 else static if (is(UT : const(ubyte)[])) 2123 tp = BSON.Type.binData; 2124 else static if (accept_ao && isArray!UT) 2125 tp = BSON.Type.array; 2126 else static if (accept_ao && isAssociativeArray!UT) 2127 tp = BSON.Type.object; 2128 else static if (accept_ao && (is(UT == class) || is(UT == struct))) 2129 tp = BSON.Type.object; 2130 else 2131 static assert(false, "Unsupported type: " ~ UT.stringof); 2132 return tp; 2133 } 2134 } 2135 2136 private BSON.Type JSONTypeToBSONType(JSON.Type tp) @safe { 2137 static immutable BSON.Type[JSON.Type.max + 1] JSONIDToBSONID = [ 2138 BSON.Type.undefined, BSON.Type.null_, BSON.Type.bool_, BSON.Type.long_, 2139 BSON.Type.long_, BSON.Type.double_, BSON.Type..string, BSON.Type.array, 2140 BSON.Type.object 2141 ]; 2142 return JSONIDToBSONID[tp]; 2143 } 2144 2145 private BSON.Type writeBSON(R)(ref R dst, in JSON value) 2146 if (isOutputRange!(R, ubyte)) { 2147 final switch (value.type) { 2148 case JSON.Type.undefined: 2149 return BSON.Type.undefined; 2150 case JSON.Type.null_: 2151 return BSON.Type.null_; 2152 case JSON.Type.bool_: 2153 dst.put(cast(ubyte)(cast(bool) value ? 0x01 : 0x00)); 2154 return BSON.Type.bool_; 2155 case JSON.Type.int_: 2156 dst.put(toBSONData(cast(long) value)); 2157 return BSON.Type.long_; 2158 case JSON.Type.bigInt: 2159 dst.put(toBSONData(cast(long) value)); 2160 return BSON.Type.long_; 2161 case JSON.Type.float_: 2162 dst.put(toBSONData(cast(double) value)); 2163 return BSON.Type.double_; 2164 case JSON.Type..string: 2165 dst.put(toBSONData(cast(uint) value.length + 1)); 2166 dst.put(cast(bdata_t) cast(string) value); 2167 dst.put(cast(ubyte) 0); 2168 return BSON.Type..string; 2169 case JSON.Type.array: 2170 auto app = appender!bdata_t(); 2171 foreach (size_t i, ref const JSON v; value) { 2172 app.put(cast(ubyte)(JSONTypeToBSONType(v.type))); 2173 putCString(app, to!string(i)); 2174 writeBSON(app, v); 2175 } 2176 2177 dst.put(toBSONData(cast(int)(app.data.length + int.sizeof + 1))); 2178 dst.put(app.data); 2179 dst.put(cast(ubyte) 0); 2180 return BSON.Type.array; 2181 case JSON.Type.object: 2182 auto app = appender!bdata_t(); 2183 foreach (string k, ref const JSON v; value) { 2184 app.put(cast(ubyte)(JSONTypeToBSONType(v.type))); 2185 putCString(app, k); 2186 writeBSON(app, v); 2187 } 2188 2189 dst.put(toBSONData(cast(int)(app.data.length + int.sizeof + 1))); 2190 dst.put(app.data); 2191 dst.put(cast(ubyte) 0); 2192 return BSON.Type.object; 2193 } 2194 } 2195 2196 unittest { 2197 JSON jsvalue = parseJSONString("{\"key\" : \"Value\"}"); 2198 assert(serializeToBSON(jsvalue).toJSON() == jsvalue); 2199 2200 jsvalue = parseJSONString("{\"key\" : [{\"key\" : \"Value\"}, {\"key2\" : \"Value2\"}] }"); 2201 assert(serializeToBSON(jsvalue).toJSON() == jsvalue); 2202 2203 jsvalue = parseJSONString("[ 1 , 2 , 3]"); 2204 assert(serializeToBSON(jsvalue).toJSON() == jsvalue); 2205 } 2206 2207 unittest { 2208 static struct Pipeline(ARGS...) { 2209 @asArray ARGS pipeline; 2210 } 2211 2212 auto getPipeline(ARGS...)(ARGS args) { 2213 return Pipeline!ARGS(args); 2214 } 2215 2216 string[string] a = ["foo" : "bar"]; 2217 int b = 42; 2218 2219 auto fields = getPipeline(a, b).serializeToBSON()["pipeline"].get!(BSON[]); 2220 assert(fields[0]["foo"].get!string == "bar"); 2221 assert(fields[1].get!int == 42); 2222 } 2223 2224 private string skipCString(ref bdata_t data) @safe { 2225 auto idx = data.countUntil(0); 2226 enforce(idx >= 0, "Unterminated BSON C-string."); 2227 auto ret = data[0 .. idx]; 2228 data = data[idx + 1 .. $]; 2229 return cast(string) ret; 2230 } 2231 2232 private void putCString(R)(ref R dst, string str) { 2233 dst.put(cast(bdata_t) str); 2234 dst.put(cast(ubyte) 0); 2235 } 2236 2237 ubyte[] toBSONData(T)(T v) { 2238 if (__ctfe) 2239 return nativeToLittleEndian(v).dup; 2240 else { 2241 static ubyte[T.sizeof] ret; 2242 ret = nativeToLittleEndian(v); 2243 return ret; 2244 } 2245 } 2246 2247 T fromBSONData(T)(in ubyte[] v) { 2248 assert(v.length >= T.sizeof); 2249 2250 ubyte[T.sizeof] vu = v[0 .. T.sizeof]; 2251 return littleEndianToNative!T(vu); 2252 } 2253 2254 ubyte[] toBigEndianData(T)(T v) { 2255 if (__ctfe) 2256 return nativeToBigEndian(v).dup; 2257 else { 2258 static ubyte[T.sizeof] ret; 2259 ret = nativeToBigEndian(v); 2260 return ret; 2261 } 2262 } 2263 2264 private string underscoreStrip(string field_name) pure @safe { 2265 if (field_name.length < 1 || field_name[$ - 1] != '_') 2266 return field_name; 2267 else 2268 return field_name[0 .. $ - 1]; 2269 } 2270 2271 /// private 2272 package template isBSONSerializable(T) { 2273 enum isBSONSerializable = is(typeof(T.init.toBSON()) : BSON) 2274 && is(typeof(T.fromBSON(BSON())) : T); 2275 }