1 /** 2 License: 3 Boost Software License - Version 1.0 - August 17th, 2003 4 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 27 Authors: 28 Alexandru Ermicioi 29 **/ 30 module aermicioi.aedi_property_reader.convertor.convertor; 31 32 import aermicioi.aedi.configurer.annotation.annotation; 33 import aermicioi.aedi_property_reader.convertor.exception : ConvertorException; 34 import aermicioi.aedi_property_reader.convertor.placeholder : identify, unwrap, unpack, pack, stored, original; 35 import aermicioi.aedi_property_reader.convertor.traits : n, PureSafeNothrowToString; 36 import taggedalgebraic : TaggedAlgebraic; 37 import std.array : array, empty, popFront, front; 38 import std.conv : text; 39 import std.exception : enforce; 40 import std.experimental.allocator : RCIAllocator, theAllocator, expandArray, make, makeArray, dispose; 41 import std.experimental.logger : trace, info, error, fatal, critical; 42 import std.meta : AliasSeq, allSatisfy, staticMap; 43 import std.range.primitives : hasLength, hasSlicing, hasAssignableElements, isOutputRange, isForwardRange, ElementType; 44 import std.range : enumerate; 45 import std.traits : isSomeFunction, Parameters; 46 import std.typecons : Flag, Yes, No; 47 import std.variant : VariantN; 48 49 50 alias ScalarConvertibleTypes = AliasSeq!( 51 ubyte, byte, ushort, short, uint, int, ulong, long, float, double, real, char, wchar, dchar 52 ); 53 54 alias StringConvertibleTypes = AliasSeq!( 55 string, wstring, dstring, char[], wchar[], dchar[] 56 ); 57 58 alias ScalarArrayConvertibleTypes = AliasSeq!( 59 ubyte[], byte[], ushort[], short[], uint[], int[], ulong[], long[], float[], double[], real[] 60 ); 61 62 alias StringArrayConvertibleTypes = AliasSeq!( // TODO add back dstring[] once std.conv is fixed. 63 string[], wstring[], char[][], wchar[][], dchar[][] 64 ); 65 66 alias MapConvertibleTypes = AliasSeq!( 67 string[string], wstring[wstring] 68 ); 69 70 alias DefaultConvertibleTypes = AliasSeq!( 71 MapConvertibleTypes, StringArrayConvertibleTypes, ScalarArrayConvertibleTypes, ScalarConvertibleTypes, StringConvertibleTypes 72 ); 73 74 /** 75 Interface for components that are able to convert from one erased type to another erased type. 76 **/ 77 interface Convertor : PureSafeNothrowToString { 78 @property { 79 80 /** 81 Get the type info of component that convertor can convert from. 82 83 Get the type info of component that convertor can convert from. 84 The method is returning the default type that it is able to convert, 85 though it is not necessarily limited to this type only. More generalistic 86 checks should be done by convertsFrom method. 87 88 Returns: 89 type info of component that convertor is able to convert. 90 **/ 91 const(TypeInfo)[] from() @safe const nothrow pure; 92 93 /** 94 Get the type info of component that convertor is able to convert to. 95 96 Get the type info of component that convertor is able to convert to. 97 The method is returning the default type that is able to convert, 98 though it is not necessarily limited to this type only. More generalistic 99 checks should be done by convertsTo method. 100 101 Returns: 102 type info of component that can be converted to. 103 **/ 104 const(TypeInfo)[] to() @safe const nothrow pure; 105 } 106 107 /** 108 Check whether convertor is able to convert from. 109 110 Check whether convertor is able to convert from. 111 The intent of method is to implement customized type checking 112 is not limited immediatly to supported default from component. 113 114 Params: 115 from = the type info of component that could potentially be converted by convertor. 116 Returns: 117 true if it is able to convert from, or false otherwise. 118 **/ 119 bool convertsFrom(const TypeInfo from) @safe const nothrow pure; 120 121 /** 122 Check whether convertor is able to convert from. 123 124 Check whether convertor is able to convert from. 125 The method will try to extract type info out of from 126 object and use for subsequent type checking. 127 The intent of method is to implement customized type checking 128 is not limited immediatly to supported default from component. 129 130 Params: 131 from = the type info of component that could potentially be converted by convertor. 132 Returns: 133 true if it is able to convert from, or false otherwise. 134 **/ 135 bool convertsFrom(in Object from) @safe const nothrow pure; 136 137 /** 138 Check whether convertor is able to convert to. 139 140 Check whether convertor is able to convert to. 141 The intent of the method is to implement customized type checking 142 that is not limited immediatly to supported default to component. 143 144 Params: 145 to = type info of component that convertor could potentially convert to. 146 147 Returns: 148 true if it is able to convert to, false otherwise. 149 **/ 150 bool convertsTo(const TypeInfo to) @safe const nothrow pure; 151 152 /** 153 Check whether convertor is able to convert to. 154 155 Check whether convertor is able to convert to. 156 The method will try to extract type info out of to object and use 157 for subsequent type checking. 158 The intent of the method is to implement customized type checking 159 that is not limited immediatly to supported default to component. 160 161 Params: 162 to = type info of component that convertor could potentially convert to. 163 164 Returns: 165 true if it is able to convert to, false otherwise. 166 **/ 167 bool convertsTo(in Object to) @safe const nothrow pure; 168 169 /** 170 Check whether convertor is able to convert from type to type. 171 172 Check whether convertor is able to convert from type to type. 173 This set of methods should be the most precise way of determining 174 whether convertor is able to convert from type to type, since it 175 provides both components to the decision logic implemented by convertor 176 compared to the case with $(D_INLINECODE convertsTo) and $(D_INLINECODE convertsFrom). 177 Note that those methods are still useful when categorization or other 178 logic should be applied per original or destination type. 179 180 Params: 181 from = the original component or it's type to convert from 182 to = the destination component or it's type to convert to 183 184 Returns: 185 true if it is able to convert from component to destination component 186 **/ 187 bool converts(const TypeInfo from, const TypeInfo to) @safe const nothrow; 188 189 /** 190 ditto 191 **/ 192 bool converts(const TypeInfo from, in Object to) @safe const nothrow; 193 194 /** 195 ditto 196 **/ 197 bool converts(in Object from, const TypeInfo to) @safe const nothrow; 198 199 /** 200 ditto 201 **/ 202 bool converts(in Object from, in Object to) @safe const nothrow; 203 204 /** 205 Check whether this convertor is able to destroy to component. 206 207 The destroys family of methods are designed purposely for identification 208 whether convertor was able to convert from type to destination to, and 209 is eligible for destruction of converted components. 210 211 Params: 212 from = original component which was converted. 213 to = converted component that should be destroyed by convertor. 214 215 Returns: 216 true if convertor is eligible for destroying to, or false otherwise. 217 **/ 218 bool destroys(const TypeInfo from, const TypeInfo to) @safe const nothrow; 219 220 /** 221 ditto 222 **/ 223 bool destroys(in Object from, const TypeInfo to) @safe const nothrow; 224 225 /** 226 ditto 227 **/ 228 bool destroys(const TypeInfo from, in Object to) @safe const nothrow; 229 230 /** 231 ditto 232 **/ 233 bool destroys(in Object from, in Object to) @safe const nothrow; 234 235 /** 236 Convert from component to component. 237 238 Params: 239 from = original component that is to be converted. 240 to = destination object that will be constructed out for original one. 241 allocator = optional allocator that could be used to construct to component. 242 Throws: 243 ConvertorException when there is a converting error 244 InvalidArgumentException when arguments passed are not of right type or state 245 Returns: 246 Resulting converted component. 247 **/ 248 Object convert(in Object from, const TypeInfo to, RCIAllocator allocator = theAllocator) const; 249 250 /** 251 Destroy component created using this convertor. 252 253 Destroy component created using this convertor. 254 Since convertor could potentially allocate memory for 255 converted component, only itself is containing history of allocation, 256 and therefore it is responsible as well to destroy and free allocated 257 memory with allocator. 258 259 Params: 260 converted = component that should be destroyed. 261 allocator = allocator used to allocate converted component. 262 Return: 263 true if component is destroyed, false otherwise 264 **/ 265 void destruct(const TypeInfo from, ref Object converted, RCIAllocator allocator = theAllocator) const; 266 } 267 268 /** 269 Default implementation of from method 270 **/ 271 mixin template FromMixin(FromTypes...) { 272 private { 273 static immutable TypeInfo[] fromTypes; 274 275 static this() { 276 TypeInfo[] infos; 277 278 static foreach (Type; FromTypes) { 279 infos ~= typeid(Type); 280 } 281 282 fromTypes = cast(immutable) infos; 283 } 284 } 285 286 @property { 287 /** 288 Get the type info of component that convertor can convert from. 289 290 Get the type info of component that convertor can convert from. 291 The method is returning the default type that it is able to convert, 292 though it is not necessarily limited to this type only. More generalistic 293 checks should be done by convertsFrom method. 294 295 Returns: 296 type info of component that convertor is able to convert. 297 **/ 298 const(TypeInfo)[] from() @safe const nothrow pure { 299 return fromTypes; 300 } 301 } 302 } 303 304 /** 305 Default implementation of converts from method 306 **/ 307 mixin template ConvertsFromMixin() { 308 import aermicioi.aedi_property_reader.convertor.placeholder : identify; 309 310 /** 311 Check whether convertor is able to convert from. 312 313 Check whether convertor is able to convert from. 314 The intent of method is to implement customized type checking 315 is not limited immediatly to supported default from component. 316 317 Params: 318 from = the type info of component that could potentially be converted by convertor. 319 Returns: 320 true if it is able to convert from, or false otherwise. 321 **/ 322 bool convertsFrom(const TypeInfo from) @safe const nothrow pure { 323 import std.algorithm; 324 return this.from.canFind!(tested => tested is from); 325 } 326 327 /** 328 Check whether convertor is able to convert from. 329 330 Check whether convertor is able to convert from. 331 The method will try to extract type info out of from 332 object and use for subsequent type checking. 333 The intent of method is to implement customized type checking 334 is not limited immediatly to supported default from component. 335 336 Params: 337 from = the type info of component that could potentially be converted by convertor. 338 Returns: 339 true if it is able to convert from, or false otherwise. 340 **/ 341 bool convertsFrom(in Object from) @safe const nothrow pure { 342 return this.convertsFrom(from.identify); 343 } 344 } 345 346 /** 347 Default implementation of to method 348 **/ 349 mixin template ToMixin(ToTypes...) { 350 private { 351 static immutable TypeInfo[] toTypes; 352 353 static this() { 354 TypeInfo[] infos; 355 356 static foreach (Type; ToTypes) { 357 infos ~= typeid(Type); 358 } 359 360 toTypes = cast(immutable) infos; 361 } 362 } 363 364 @property { 365 /** 366 Get the type info of component that convertor is able to convert to. 367 368 Get the type info of component that convertor is able to convert to. 369 The method is returning the default type that is able to convert, 370 though it is not necessarily limited to this type only. More generalistic 371 checks should be done by convertsTo method. 372 373 Returns: 374 type info of component that can be converted to. 375 **/ 376 const(TypeInfo)[] to() @safe const nothrow pure { 377 return this.toTypes; 378 } 379 } 380 } 381 382 /** 383 Default implementation of converts to method 384 **/ 385 mixin template ConvertsToMixin() { 386 /** 387 Check whether convertor is able to convert to. 388 389 Check whether convertor is able to convert to. 390 The intent of the method is to implement customized type checking 391 that is not limited immediatly to supported default to component. 392 393 Params: 394 to = type info of component that convertor could potentially convert to. 395 396 Returns: 397 true if it is able to convert to, false otherwise. 398 **/ 399 bool convertsTo(const TypeInfo to) @safe const nothrow pure { 400 import std.algorithm; 401 return this.to.canFind!(tested => tested is to); 402 } 403 404 /** 405 Check whether convertor is able to convert to. 406 407 Check whether convertor is able to convert to. 408 The method will try to extract type info out of to object and use 409 for subsequent type checking. 410 The intent of the method is to implement customized type checking 411 that is not limited immediatly to supported default to component. 412 413 Params: 414 to = type info of component that convertor could potentially convert to. 415 416 Returns: 417 true if it is able to convert to, false otherwise. 418 **/ 419 bool convertsTo(in Object to) @safe const nothrow pure { 420 return this.convertsTo(to.identify); 421 } 422 } 423 424 /** 425 Default implementation of converts method. 426 **/ 427 mixin template ConvertsMixin() { 428 /** 429 Check whether convertor is able to convert from type to type. 430 431 Check whether convertor is able to convert from type to type. 432 This set of methods should be the most precise way of determining 433 whether convertor is able to convert from type to type, since it 434 provides both components to the decision logic implemented by convertor 435 compared to the case with $(D_INLINECODE convertsTo) and $(D_INLINECODE convertsFrom). 436 Note that those methods are still useful when categorization or other 437 logic should be applied per original or destination type. 438 439 Implementation: 440 This is default implementation of converts methods which delegate 441 the decision to $(D_INLINECODE convertsTo) and $(D_INLINECODE convertsFrom). 442 443 Params: 444 from = the original component or it's type to convert from 445 to = the destination component or it's type to convert to 446 447 Returns: 448 true if it is able to convert from component to destination component 449 **/ 450 bool converts(const TypeInfo from, const TypeInfo to) @safe const nothrow { 451 return this.convertsFrom(from) && this.convertsTo(to); 452 } 453 454 /** 455 ditto 456 **/ 457 bool converts(const TypeInfo from, in Object to) @safe const nothrow { 458 return this.convertsFrom(from) && this.convertsTo(to); 459 } 460 461 /** 462 ditto 463 **/ 464 bool converts(in Object from, const TypeInfo to) @safe const nothrow { 465 return this.convertsFrom(from) && this.convertsTo(to); 466 } 467 468 /** 469 ditto 470 **/ 471 bool converts(in Object from, in Object to) @safe const nothrow { 472 return this.convertsFrom(from) && this.convertsTo(to); 473 } 474 } 475 476 /** 477 Default implementation of destroys method. 478 **/ 479 mixin template DestroysMixin() { 480 import aermicioi.aedi_property_reader.convertor.placeholder : original; 481 482 /** 483 Check whether this convertor is able to destroy to component. 484 485 The destroys family of methods are designed purposely for identification 486 whether convertor was able to convert from type to destination to, and 487 is eligible for destruction of converted components. 488 489 Params: 490 from = original component which was converted. 491 to = converted component that should be destroyed by convertor. 492 493 Returns: 494 true if convertor is eligible for destroying to, or false otherwise. 495 **/ 496 bool destroys(const TypeInfo from, const TypeInfo to) @safe const nothrow { 497 return this.convertsFrom(from) && this.convertsTo(to); 498 } 499 500 /** 501 ditto 502 **/ 503 bool destroys(in Object from, const TypeInfo to) @safe const nothrow { 504 return this.convertsFrom(from) && this.convertsTo(to); 505 } 506 507 /** 508 ditto 509 **/ 510 bool destroys(const TypeInfo from, in Object to) @safe const nothrow { 511 if (this.convertsFrom(from) && this.convertsTo(to)) { 512 if (to.original !is typeid(void)) { 513 return this.convertsFrom(to.original); 514 } 515 516 return true; 517 } 518 519 return false; 520 } 521 522 /** 523 ditto 524 **/ 525 bool destroys(in Object from, in Object to) @safe const nothrow { 526 if (this.convertsFrom(from) && this.convertsTo(to)) { 527 if (to.original !is typeid(void)) { 528 return this.convertsFrom(to.original); 529 } 530 531 return true; 532 } 533 534 return false; 535 } 536 } 537 538 mixin template ConvertsFromToMixin(FromType, ToType) { 539 import aermicioi.aedi_property_reader.convertor.convertor : FromMixin, ToMixin, ConvertsFromMixin, ConvertsToMixin, ConvertsMixin, DestroysMixin; 540 541 mixin FromMixin!FromType; 542 mixin ToMixin!ToType; 543 mixin ConvertsFromMixin; 544 mixin ConvertsToMixin; 545 mixin ConvertsMixin; 546 mixin DestroysMixin; 547 } 548 549 550 mixin template ConvertsFromToMixin() { 551 import aermicioi.aedi_property_reader.convertor.convertor : ConvertsFromMixin, ConvertsToMixin, ConvertsMixin, DestroysMixin; 552 553 mixin ConvertsFromMixin; 554 mixin ConvertsToMixin; 555 mixin ConvertsMixin; 556 mixin DestroysMixin; 557 } 558 559 /** 560 Mixin that provides default implementation of equality comparison for convertors 561 **/ 562 mixin template EqualMixin() { 563 564 override bool opEquals(Object o) { 565 return super.opEquals(o) || ((this.classinfo is o.classinfo) && this.opEquals(cast(Convertor) o)); 566 } 567 568 bool opEquals(Convertor convertor) { 569 if (convertor is null) { 570 return false; 571 } 572 573 if ( 574 (this.from.length != convertor.from.length) || 575 (this.to.length != convertor.to.length) 576 ) { 577 return false; 578 } 579 580 foreach (index, from; this.from) { 581 if (from !is convertor.from[index]) { 582 return false; 583 } 584 } 585 586 foreach (index, to; this.to) { 587 if (to !is convertor.to[index]) { 588 return false; 589 } 590 } 591 592 return true; 593 } 594 } 595 596 /** 597 Mixin that provides default implementation of hashing algorithm 598 **/ 599 mixin template ToHashMixin() { 600 601 override size_t toHash() { 602 import std.range : only, chain; 603 import std.algorithm : map; 604 size_t result = 7; 605 606 foreach (hash; chain(this.from, this.to, this.classinfo.only).map!(ti => ti.toHash)) { 607 result = result * 31 + hash; 608 } 609 610 return result; 611 } 612 } 613 614 /** 615 Mixin that provides default implementation of toString method 616 **/ 617 mixin template ToStringMixin() { 618 619 override string toString() @safe nothrow pure { 620 import std..string : lastIndexOf; 621 import std.conv : text; 622 623 try { 624 auto identity = typeid(this).name[typeid(this).name.lastIndexOf(".") + 1 .. $]; 625 auto from = this.from.length > 2 ? text(this.from[0 .. 2], "...") : text(this.from); 626 auto to = this.to.length > 2 ? text(this.to[0 .. 2], "...") : text(this.to); 627 return text( 628 identity, " [ from: ", from, ", to: ", to, "]" 629 ); 630 } catch (Exception e) { 631 throw new Error("Failed to convert component to string, due to an exception.", e); 632 } 633 } 634 } 635 636 /** 637 Default implementation of comparison between two or more convertors. 638 **/ 639 mixin template OpCmpMixin() { 640 641 override int opCmp(Object o) const { 642 643 auto result = cast(int)cast(void*)this - cast(int)cast(void*)o; 644 645 if (result == 0) { 646 result = 647 this.classinfo.name < o.classinfo.name ? -1 : 648 (this.classinfo.name > o.classinfo.name ? 1 : 0); 649 } 650 651 return result; 652 } 653 } 654 655 /** 656 Amalgation of EqualMixin, ToHashMixin, ToStringMixin, and OpCmpMixin 657 **/ 658 mixin template EqualToHashToStringOpCmpMixin() { 659 import aermicioi.aedi_property_reader.convertor.convertor : EqualMixin, ToHashMixin, ToStringMixin, OpCmpMixin; 660 661 mixin EqualMixin!(); 662 mixin ToHashMixin!(); 663 mixin ToStringMixin!(); 664 mixin OpCmpMixin!(); 665 } 666 667 /** 668 A convertor able to unwrap an element out of TaggedAlgebraic!Union. 669 **/ 670 class TaggedConvertor(Tagged : TaggedAlgebraic!Union, Union) : Convertor { 671 import std.traits : Fields, FieldNameTuple; 672 673 mixin FromMixin!Tagged; 674 mixin ToMixin!(Fields!Union); 675 676 mixin ConvertsFromToMixin; 677 678 public { 679 680 /** 681 ditto 682 **/ 683 bool converts(in Object from, const TypeInfo to) @safe const nothrow { 684 if (this.convertsFrom(from)) { 685 auto tagged = from.unwrap!Tagged; 686 687 static foreach (field; FieldNameTuple!Union) { 688 if (tagged.kind == mixin("Tagged.Kind." ~ field)) { 689 return mixin("typeid(typeof(Union." ~ field ~ ")) is to"); 690 } 691 } 692 } 693 694 return false; 695 } 696 697 /** 698 ditto 699 **/ 700 bool converts(in Object from, in Object to) @safe const nothrow { 701 return this.converts(from, to.identify); 702 } 703 704 /** 705 Convert from component to component. 706 707 Convert from component to component. 708 In case when functional convertor's from argument is rooted in Object 709 class, the convertor will attempt to downcast passed component to 710 it's rightfull type. If not, the convertor will attempt to downcast 711 from component to placeholding object of value that is accepted by functional 712 convertor. 713 For converted to component, in case when it is rooted in Object class 714 nothing special is performed, while otherwise it will be wrapped into placeholding 715 object allocated by allocator that will be returned to callee. 716 717 Params: 718 from = original component that is to be converted. 719 to = destination object that will be constructed out for original one. 720 allocator = optional allocator that could be used to construct to component. 721 Throws: 722 ConvertorException when convertor is not able to convert from, or to component. 723 Returns: 724 Resulting converted component. 725 **/ 726 Object convert(in Object from, const TypeInfo to, RCIAllocator allocator = theAllocator) const 727 { 728 enforce!ConvertorException(this.converts(from, to), text( 729 "Cannot convert component ", from.identify, " to ", to, " expected original component of ", this.from, " and destination of ", this.to 730 )); 731 732 auto tagged = from.unwrap!Tagged; 733 734 static foreach (field; FieldNameTuple!Union) { 735 if (tagged.kind == mixin("Tagged.Kind." ~ field)) { 736 737 debug(trace) trace("Converting ", tagged, " to ", to); 738 return mixin("(cast(typeof(Union." ~ field ~ ")) tagged).pack(from, this, allocator)"); 739 } 740 } 741 742 throw new ConvertorException(text("Could not unwrap tagged element ", from.identify, " to ", to, " expected ")); 743 } 744 745 /** 746 Destroy component created using this convertor. 747 748 Destroy component created using this convertor. 749 The method will attempt to downcast converted component 750 to type supported by functional destructor when the type is rooted in 751 Object class, otherwise it will attempt to downcast converted to placeholding 752 object of value supported by functional destructor. In latest case, the 753 method will dispose out of placeholding object using passed allocator. 754 755 Params: 756 converted = component that should be destroyed. 757 allocator = allocator used to allocate converted component. 758 **/ 759 void destruct(const TypeInfo from, ref Object converted, RCIAllocator allocator = theAllocator) const { 760 enforce!ConvertorException(this.destroys(from, converted), text( 761 "Cannot destroy ", converted.identify, " which was not converted from ", from, ".", 762 " Expected destroyable type of ", this.to, " from origin of ", this.from 763 )); 764 765 static foreach(Type; Fields!Union) { 766 if (converted.identify is typeid(Type)) { 767 768 debug(trace) trace("Disposing of container for type ", typeid(Type), " if it is value type"); 769 converted.unpack!Type(allocator); 770 } 771 } 772 } 773 774 mixin EqualToHashToStringOpCmpMixin!(); 775 } 776 } 777 778 /** 779 Interface for convertors that could rely on another convertors to convert a value. 780 **/ 781 interface CombinedConvertor : Convertor { 782 783 /** 784 Set used convertors 785 786 Params: 787 convertors = list of convertors to be used. 788 789 Returns: 790 typeof(this) 791 **/ 792 typeof(this) convertors(Convertor[] convertors) @safe nothrow; 793 794 /** 795 ditto 796 **/ 797 T convertors(this T)(Convertor convertors...) @safe nothrow { 798 return this.convertors(convertors.dup); 799 } 800 801 /** 802 Add a convertor to existing list 803 804 Params: 805 convertor = convertor to be added to 806 807 Returns: 808 typeof(this) 809 **/ 810 typeof(this) add(Convertor convertor) @safe nothrow; 811 812 /** 813 Remove a convertor from existing list 814 815 Params: 816 convertor = convertor to be removed 817 818 Returns: 819 typeof(this) 820 **/ 821 typeof(this) remove(Convertor convertor) @safe nothrow; 822 } 823 824 /** 825 A convertor that is delegating converting task to a set of child convertors. 826 **/ 827 class CombinedConvertorImpl : CombinedConvertor { 828 import std.algorithm : canFind, find, map, joiner, filter, any; 829 830 private { 831 Convertor[] convertors_; 832 833 private const(TypeInfo)[] fromTypes; 834 private const(TypeInfo)[] toTypes; 835 } 836 837 public { 838 839 /** 840 Default constructor for AggregateConvertor 841 **/ 842 @safe this(Convertor[] convertors...) { 843 this.convertors = convertors.dup; 844 } 845 846 /** 847 Set convertors 848 849 Params: 850 convertors = convertors used to convert from one type to another 851 852 Returns: 853 typeof(this) 854 **/ 855 typeof(this) convertors(Convertor[] convertors) @trusted nothrow pure 856 in (!convertors.empty, "Combined convertor is expecting to have at least one convertor passed to it. None passed") 857 in (convertors.all!(c => c !is null), "Encountered null convertor in convertor list received in combined convertor, none are expected") { 858 this.convertors_ = convertors; 859 860 this.fromTypes = null; 861 this.toTypes = null; 862 863 foreach (convertor; convertors) { 864 this.fromTypes ~= convertors.map!(convertor => convertor.from).joiner.filter!(type => this.fromTypes.canFind!(from => type is from)).array; 865 this.toTypes ~= convertors.map!(convertor => convertor.to).joiner.filter!(type => this.fromTypes.canFind!(from => type is from)).array; 866 } 867 868 return this; 869 } 870 871 /** 872 Get convertors 873 874 Returns: 875 Convertor[] 876 **/ 877 inout(Convertor[]) convertors() @safe nothrow pure inout { 878 return this.convertors_; 879 } 880 881 /** 882 Add a convertor to existing list 883 884 Params: 885 convertor = convertor to be added to 886 887 Returns: 888 typeof(this) 889 **/ 890 typeof(this) add(Convertor convertor) @trusted nothrow 891 in (convertor !is null, "Cannot add null convertor into combined convertor") { 892 this.convertors_ ~= convertor; 893 894 this.fromTypes ~= convertor.from.filter!(type => !this.fromTypes.canFind!(from => type is from)).array; 895 this.toTypes ~= convertor.from.filter!(type => !this.toTypes.canFind!(to => type is to)).array; 896 897 return this; 898 } 899 900 /** 901 Remove a convertor from existing list 902 903 Params: 904 convertor = convertor to be removed 905 906 Returns: 907 typeof(this) 908 **/ 909 typeof(this) remove(Convertor convertor) @trusted nothrow { 910 import std.algorithm : remove, countUntil; 911 import std.array : array; 912 913 try { 914 915 this.convertors_ = this.convertors_.remove(this.convertors.countUntil!(c => c == convertor)); 916 this.fromTypes = this.fromTypes.filter!( 917 from => !this.convertors.any!(convertor => convertor.convertsFrom(from)) 918 ).array; 919 920 this.toTypes = this.fromTypes.filter!( 921 to => !this.convertors.any!(convertor => convertor.convertsTo(to)) 922 ).array; 923 924 } catch (Exception e) { 925 assert(false, text("countUntil threw an exception: ", e)); 926 } 927 928 return this; 929 } 930 931 @property { 932 /** 933 Get the type info of component that convertor can convert from. 934 935 Get the type info of component that convertor can convert from. 936 The method is returning the default type that it is able to convert, 937 though it is not necessarily limited to this type only. More generalistic 938 checks should be done by convertsFrom method. 939 940 Returns: 941 type info of component that convertor is able to convert. 942 **/ 943 const(TypeInfo)[] from() @safe nothrow pure const { 944 return fromTypes; 945 } 946 947 /** 948 Get the type info of component that convertor is able to convert to. 949 950 Get the type info of component that convertor is able to convert to. 951 The method is returning the default type that is able to convert, 952 though it is not necessarily limited to this type only. More generalistic 953 checks should be done by convertsTo method. 954 955 Returns: 956 type info of component that can be converted to. 957 **/ 958 const(TypeInfo)[] to() @safe nothrow pure const { 959 return toTypes; 960 } 961 } 962 963 /** 964 Check whether convertor is able to convert from. 965 966 Params: 967 from = the type info of component that could potentially be converted by convertor. 968 Returns: 969 true if it is able to convert from, or false otherwise. 970 **/ 971 bool convertsFrom(const TypeInfo from) const { 972 return this.convertors.canFind!(c => c.convertsFrom(from)); 973 } 974 975 /** 976 ditto 977 **/ 978 bool convertsFrom(in Object from) const { 979 return this.convertors.canFind!(c => c.convertsFrom(from)); 980 } 981 982 /** 983 Check whether convertor is able to convert to. 984 985 Params: 986 to = type info of component that convertor could potentially convert to. 987 988 Returns: 989 true if it is able to convert to, false otherwise. 990 **/ 991 bool convertsTo(const TypeInfo to) const { 992 return this.convertors.canFind!(c => c.convertsTo(to)); 993 } 994 995 /** 996 ditto 997 **/ 998 bool convertsTo(in Object to) const { 999 return this.convertors.canFind!(c => c.convertsTo(to)); 1000 } 1001 1002 /** 1003 Check whether convertor is able to convert from type to type. 1004 1005 Check whether convertor is able to convert from type to type. 1006 This set of methods should be the most precise way of determining 1007 whether convertor is able to convert from type to type, since it 1008 provides both components to the decision logic implemented by convertor 1009 compared to the case with $(D_INLINECODE convertsTo) and $(D_INLINECODE convertsFrom). 1010 Note that those methods are still useful when categorization or other 1011 logic should be applied per original or destination type. 1012 1013 Implementation: 1014 This is default implementation of converts methods which delegate 1015 the decision to $(D_INLINECODE convertsTo) and $(D_INLINECODE convertsFrom). 1016 1017 Params: 1018 from = the original component or it's type to convert from 1019 to = the destination component or it's type to convert to 1020 1021 Returns: 1022 true if it is able to convert from component to destination component 1023 **/ 1024 bool converts(const TypeInfo from, const TypeInfo to) @safe const nothrow { 1025 return this.convertors.canFind!(convertor => convertor.converts(from, to)); 1026 } 1027 1028 /** 1029 ditto 1030 **/ 1031 bool converts(const TypeInfo from, in Object to) @safe const nothrow { 1032 return this.convertors.canFind!(convertor => convertor.converts(from, to)); 1033 } 1034 1035 /** 1036 ditto 1037 **/ 1038 bool converts(in Object from, const TypeInfo to) @safe const nothrow { 1039 return this.convertors.canFind!(convertor => convertor.converts(from, to)); 1040 } 1041 1042 /** 1043 ditto 1044 **/ 1045 bool converts(in Object from, in Object to) @safe const nothrow { 1046 return this.convertors.canFind!(convertor => convertor.converts(from, to)); 1047 } 1048 1049 /** 1050 Check whether this convertor is able to destroy to component. 1051 1052 The destroys family of methods are designed purposely for identification 1053 whether convertor was able to convert from type to destination to, and 1054 is eligible for destruction of converted components. 1055 1056 Params: 1057 from = original component which was converted. 1058 to = converted component that should be destroyed by convertor. 1059 1060 Returns: 1061 true if convertor is eligible for destroying to, or false otherwise. 1062 **/ 1063 bool destroys(const TypeInfo from, const TypeInfo to) @safe const nothrow { 1064 return this.convertors.canFind!(convertor => convertor.destroys(from, to)); 1065 } 1066 1067 /** 1068 ditto 1069 **/ 1070 bool destroys(in Object from, const TypeInfo to) @safe const nothrow { 1071 return this.convertors.canFind!(convertor => convertor.destroys(from, to)); 1072 } 1073 1074 /** 1075 ditto 1076 **/ 1077 bool destroys(const TypeInfo from, in Object to) @safe const nothrow { 1078 return this.convertors.canFind!(convertor => convertor.destroys(from, to)); 1079 } 1080 1081 /** 1082 ditto 1083 **/ 1084 bool destroys(in Object from, in Object to) @safe const nothrow { 1085 return this.convertors.canFind!(convertor => convertor.destroys(from, to)); 1086 } 1087 1088 /** 1089 Convert from component to component. 1090 1091 Finds a right convertor from component to component and uses it 1092 to execute conversion from component to component. 1093 1094 Params: 1095 from = original component that is to be converted. 1096 to = destination object that will be constructed out for original one. 1097 allocator = optional allocator that could be used to construct to component. 1098 Throws: 1099 ConvertorException when convertor is not able to convert from, or to component. 1100 Returns: 1101 Resulting converted component. 1102 **/ 1103 Object convert(in Object from, const TypeInfo to, RCIAllocator allocator = theAllocator) const 1104 { 1105 auto convertors = this.convertors.find!(c => c.converts(from, to)); 1106 1107 if (!convertors.empty) { 1108 debug(trace) trace("Converting ", from.identify, " to ", to, " using convertor ", convertors.front); 1109 return convertors.front.convert(from, to, allocator); 1110 } 1111 1112 throw new ConvertorException(text("Could not convert ", typeid(from), " to type ", to)); 1113 } 1114 1115 /** 1116 Destroy component created using this convertor. 1117 1118 Find a suitable convertor for destruction and use it to execute destruction. 1119 1120 Params: 1121 converted = component that should be destroyed. 1122 allocator = allocator used to allocate converted component. 1123 **/ 1124 void destruct(const TypeInfo from, ref Object converted, RCIAllocator allocator = theAllocator) const { 1125 enforce!ConvertorException(this.destroys(from, converted), text( 1126 "Cannot destroy ", converted.identify, " which was not converted from ", from, ".", 1127 " Expected destroyable type of ", this.to, " from origin of ", this.from 1128 )); 1129 1130 auto convertors = this.convertors.find!(c => c.destroys(from, converted)); 1131 1132 if (convertors.empty) { 1133 throw new ConvertorException(text("No convertor was found to be able to destroy component of ", converted.identify, " converted from ", from)); 1134 } 1135 1136 debug(trace) trace("Destroying ", converted.identify, " converted from ", from, " using convertor ", convertors.front); 1137 convertors.front.destruct(from, converted, allocator); 1138 } 1139 1140 mixin EqualToHashToStringOpCmpMixin!(); 1141 } 1142 } 1143 1144 /** 1145 A convertor that simply doesn't do any conversion and returns existing object 1146 **/ 1147 class NoOpConvertor : Convertor { 1148 1149 public { 1150 mixin FromMixin; 1151 mixin ToMixin; 1152 mixin ConvertsFromToMixin; 1153 1154 /** 1155 Convert from component to component. 1156 1157 It will simply pass existing from component as to component 1158 as consequence requested to component should be same as type info 1159 of from component. 1160 1161 Params: 1162 from = original component that is to be converted. 1163 to = destination object that will be constructed out for original one. 1164 allocator = optional allocator that could be used to construct to component. 1165 Throws: 1166 ConvertorException when convertor is not able to convert from, or to component. 1167 Returns: 1168 Resulting converted component. 1169 **/ 1170 Object convert(in Object from, const TypeInfo to, RCIAllocator allocator = theAllocator) const 1171 { 1172 enforce!ConvertorException( 1173 from.identify is to, 1174 text( 1175 "Cannot do no op when expected ", 1176 to, 1177 " type is not of same type as original from ", 1178 from.identify, 1179 " type" 1180 ) 1181 ); 1182 1183 return cast() from; 1184 } 1185 1186 /** 1187 Does not destruct anything since it is not allocating anything. 1188 1189 Params: 1190 converted = component that should be destroyed. 1191 allocator = allocator used to allocate converted component. 1192 **/ 1193 void destruct(const TypeInfo from, ref Object converted, RCIAllocator allocator = theAllocator) const { 1194 1195 } 1196 1197 mixin EqualToHashToStringOpCmpMixin!(); 1198 } 1199 } 1200 1201 /** 1202 A convertor that converts a elements of a forward range and puts into an output range that is also a forward range. 1203 **/ 1204 @component 1205 class RangeConvertor(To, From) : Convertor 1206 if ( 1207 isForwardRange!From && 1208 isForwardRange!To && 1209 __traits(hasMember, To, "put") 1210 ) { 1211 1212 private { 1213 alias InputType = ElementType!From; 1214 alias OutputType = Parameters!(To.put)[0]; 1215 1216 Convertor convertor_; 1217 } 1218 1219 public { 1220 @property { 1221 /** 1222 Set convertor 1223 1224 Params: 1225 convertor = convertor used to convert elements from input range and put them into output range. 1226 1227 Returns: 1228 typeof(this) 1229 **/ 1230 @autowired 1231 typeof(this) convertor(Convertor defaultConvertor) @safe nothrow pure 1232 in (defaultConvertor !is null, "Cannot accept a null as a convertor, expected one that converts range " ~ typeid(From).toString ~ " to " ~ typeid(To).toString) 1233 in (defaultConvertor.converts(typeid(InputType), typeid(OutputType)), "Passed convertor is not able to convert range elements from " ~ typeid(InputType).toString ~ " to " ~ typeid(OutputType).toString) { 1234 this.convertor_ = defaultConvertor; 1235 1236 return this; 1237 } 1238 1239 /** 1240 Get convertor 1241 1242 Returns: 1243 Convertor 1244 **/ 1245 inout(Convertor) convertor() @safe nothrow pure inout { 1246 return this.convertor_; 1247 } 1248 } 1249 1250 mixin ConvertsFromToMixin!(From, To) DefaultImplementation; 1251 1252 /** 1253 ditto 1254 **/ 1255 bool converts(in Object from, const TypeInfo to) @safe const nothrow { 1256 return this.convertsFrom(from) && this.convertsTo(to); 1257 } 1258 1259 /** 1260 ditto 1261 **/ 1262 bool converts(in Object from, in Object to) @safe const nothrow { 1263 return this.convertsFrom(from) && this.convertsTo(to); 1264 } 1265 1266 /** 1267 Convert from component to component. 1268 1269 It will simply pass existing from component as to component 1270 as consequence requested to component should be same as type info 1271 of from component. 1272 1273 Params: 1274 from = original component that is to be converted. 1275 to = destination object that will be constructed out for original one. 1276 allocator = optional allocator that could be used to construct to component. 1277 Throws: 1278 ConvertorException when convertor is not able to convert from, or to component. 1279 Returns: 1280 Resulting converted component. 1281 **/ 1282 Object convert(in Object from, const TypeInfo to, RCIAllocator allocator = theAllocator) const 1283 { 1284 enforce!ConvertorException( 1285 this.convertsFrom(from), 1286 "Cannot transfer contents of a range ", from.identify, " to output range. Expected range of ", typeid(From) 1287 ); 1288 1289 enforce!ConvertorException( 1290 this.convertsTo(to), 1291 "Cannot transfer contents of range to ", to, " expected an output range of ", typeid(To) 1292 ); 1293 1294 To output; 1295 1296 static if (is(To == class)) { 1297 output = allocator.make!To(); 1298 } else { 1299 output = To(); 1300 } 1301 1302 From source = from.unwrap!From; 1303 1304 debug(trace) trace("Converting range ", from.identify, " to ", to); 1305 foreach (element; source) { 1306 auto temporary = source.stored(from, this); 1307 output.put(this.convertor.convert(temporary, typeid(OutputType), allocator).unpack!OutputType(allocator)); 1308 } 1309 1310 return output.pack(from, this, allocator); 1311 } 1312 1313 /** 1314 Does not destruct anything since it is not allocating anything. 1315 1316 Params: 1317 converted = component that should be destroyed. 1318 allocator = allocator used to allocate converted component. 1319 **/ 1320 void destruct(const TypeInfo from, ref Object converted, RCIAllocator allocator = theAllocator) const { 1321 enforce!ConvertorException(this.destroys(from, converted), text( 1322 "Cannot destroy ", converted.identify, " which was not converted from ", from, ".", 1323 " Expected destroyable type of ", this.to, " from origin of ", this.from 1324 )); 1325 1326 To destroyable = converted.unpack!To(allocator); 1327 1328 debug(trace) trace("Destroying converted range ", converted.identify, " constructed from ", from); 1329 foreach (element; destroyable) { 1330 auto temporary = element.stored(from, this); 1331 Object reference = temporary; 1332 this.convertor.destruct(from, reference, allocator); 1333 } 1334 } 1335 1336 mixin EqualToHashToStringOpCmpMixin!(); 1337 } 1338 } 1339 1340 /** 1341 A convertor that converts a forward range into an array of it's elements converted to a destination type. 1342 **/ 1343 @component 1344 class RangeToArrayConvertor(To : Element[], From, Element) : Convertor 1345 if ( 1346 isForwardRange!From 1347 ) { 1348 1349 private { 1350 alias InputType = ElementType!From; 1351 1352 Convertor convertor_; 1353 } 1354 1355 public { 1356 @autowired 1357 this(Convertor convertor) { 1358 this.convertor = convertor; 1359 } 1360 1361 @property { 1362 /** 1363 Set convertor 1364 1365 Params: 1366 convertor = convertor used to convert elements from input range and put them into output range. 1367 1368 Returns: 1369 typeof(this) 1370 **/ 1371 @autowired 1372 typeof(this) convertor(Convertor defaultConvertor) @safe nothrow 1373 in (defaultConvertor !is null, "Cannot accept a null as a convertor, expected one that converts range " ~ typeid(From).toString ~ " to " ~ typeid(To).toString) 1374 in (defaultConvertor.converts(typeid(InputType), typeid(Element)), "Passed convertor is not able to convert range elements from " ~ typeid(InputType).toString ~ " to " ~ typeid(Element).toString) { 1375 this.convertor_ = defaultConvertor; 1376 1377 return this; 1378 } 1379 1380 /** 1381 Get convertor 1382 1383 Returns: 1384 Convertor 1385 **/ 1386 inout(Convertor) convertor() @safe nothrow pure inout { 1387 return this.convertor_; 1388 } 1389 } 1390 1391 mixin ConvertsFromToMixin!(From, To); 1392 1393 /** 1394 Convert from component to component. 1395 1396 It will simply pass existing from component as to component 1397 as consequence requested to component should be same as type info 1398 of from component. 1399 1400 Params: 1401 from = original component that is to be converted. 1402 to = destination object that will be constructed out for original one. 1403 allocator = optional allocator that could be used to construct to component. 1404 Throws: 1405 ConvertorException when convertor is not able to convert from, or to component. 1406 Returns: 1407 Resulting converted component. 1408 **/ 1409 Object convert(in Object from, const TypeInfo to, RCIAllocator allocator = theAllocator) const 1410 { 1411 enforce!ConvertorException( 1412 this.convertsFrom(from), 1413 text("Cannot transfer contents of a range ", from.identify, " to output range. Expected range of ", typeid(From)) 1414 ); 1415 1416 enforce!ConvertorException( 1417 this.convertsTo(to), 1418 text("Cannot transfer contents of range to ", to, " expected an array of ", typeid(Element)) 1419 ); 1420 1421 To output; 1422 From source = from.unwrap!From; 1423 1424 if (source.empty) { 1425 return output.pack(from.identify, this, allocator); 1426 } 1427 1428 static if (hasLength!From) { 1429 output = allocator.makeArray!Element(source.length); 1430 } else { 1431 output = allocator.makeArray!Element(1); 1432 } 1433 1434 debug(trace) trace("Converting range ", from.identify, " to an array of type ", to); 1435 foreach (indexed; source.enumerate) { 1436 if (indexed.index == output.length) { 1437 allocator.expandArray(output, 1); 1438 } 1439 1440 auto temporary = indexed.value.stored(from.identify, this); 1441 output[indexed.index] = this.convertor.convert(temporary, typeid(Element), allocator).unpack!Element(allocator); 1442 } 1443 1444 return output.pack(from.identify, this, allocator); 1445 } 1446 1447 /** 1448 Does not destruct anything since it is not allocating anything. 1449 1450 Params: 1451 converted = component that should be destroyed. 1452 allocator = allocator used to allocate converted component. 1453 **/ 1454 void destruct(const TypeInfo from, ref Object converted, RCIAllocator allocator = theAllocator) const { 1455 enforce!ConvertorException(this.destroys(from, converted), text( 1456 "Cannot destroy ", converted.identify, " which was not converted from ", from, ".", 1457 " Expected destroyable type of ", this.to, " from origin of ", this.from 1458 )); 1459 1460 To destroyable = converted.unpack!To(allocator); 1461 1462 debug(trace) trace("Destroying array ", to, " converted from original range of type ", from); 1463 foreach (element; destroyable) { 1464 auto temporary = element.stored(from.identify, this); 1465 Object reference = temporary; 1466 this.convertor.destruct(from, reference, allocator); 1467 } 1468 1469 allocator.dispose(destroyable); 1470 } 1471 1472 mixin EqualToHashToStringOpCmpMixin!(); 1473 } 1474 } 1475 1476 /** 1477 A convertor that converts a forward range into an array of it's elements converted to a destination type. 1478 **/ 1479 @component 1480 class MapConvertor(To : ToElement[ToKey], From : FromElement[FromKey], ToElement, ToKey, FromElement, FromKey) : Convertor { 1481 1482 private { 1483 alias InputType = ElementType!From; 1484 1485 Convertor elementConvertor_; 1486 Convertor keyConvertor_; 1487 } 1488 1489 public { 1490 @property { 1491 /** 1492 Set convertor 1493 1494 Params: 1495 convertor = convertor used to convert elements from input range and put them into output range. 1496 1497 Returns: 1498 typeof(this) 1499 **/ 1500 @autowired 1501 typeof(this) elementConvertor(Convertor elementConvertor) @safe nothrow pure 1502 in (elementConvertor !is null, "Cannot accept a null as a convertor, expected one that map value " ~ typeid(FromElement) ~ " to " ~ typeid(ToElement)) 1503 in (elementConvertor.converts(typeid(FromElement), typeid(ToElement)), "Passed convertor is not able to convert map value from " ~ typeid(FromElement) ~ " to " ~ typeid(ToElement)) { 1504 this.elementConvertor_ = elementConvertor; 1505 1506 return this; 1507 } 1508 1509 /** 1510 Get convertor 1511 1512 Returns: 1513 Convertor 1514 **/ 1515 inout(Convertor) elementConvertor() @safe nothrow pure inout { 1516 return this.elementConvertor_; 1517 } 1518 1519 /** 1520 Set convertor 1521 1522 Params: 1523 convertor = convertor used to convert elements from input range and put them into output range. 1524 1525 Returns: 1526 typeof(this) 1527 **/ 1528 @autowired 1529 typeof(this) keyConvertor(Convertor keyConvertor) @safe nothrow pure 1530 in (keyConvertor !is null, "Cannot accept a null as a convertor, expected one that map key " ~ typeid(FromKey) ~ " to " ~ typeid(ToKey)) 1531 in (keyConvertor.converts(typeid(FromElement), typeid(ToElement)), "Passed convertor is not able to convert map key from " ~ typeid(FromKey) ~ " to " ~ typeid(ToKey)) { 1532 this.keyConvertor_ = keyConvertor; 1533 1534 return this; 1535 } 1536 1537 /** 1538 Get convertor 1539 1540 Returns: 1541 Convertor 1542 **/ 1543 inout(Convertor) keyConvertor() @safe nothrow pure inout { 1544 return this.keyConvertor_; 1545 } 1546 1547 /** 1548 Get the type info of component that convertor can convert from. 1549 1550 Get the type info of component that convertor can convert from. 1551 The method is returning the default type that it is able to convert, 1552 though it is not necessarily limited to this type only. More generalistic 1553 checks should be done by convertsFrom method. 1554 1555 Returns: 1556 type info of component that convertor is able to convert. 1557 **/ 1558 TypeInfo from() @safe nothrow pure const { 1559 return typeid(From); 1560 } 1561 1562 /** 1563 Get the type info of component that convertor is able to convert to. 1564 1565 Get the type info of component that convertor is able to convert to. 1566 The method is returning the default type that is able to convert, 1567 though it is not necessarily limited to this type only. More generalistic 1568 checks should be done by convertsTo method. 1569 1570 Returns: 1571 type info of component that can be converted to. 1572 **/ 1573 TypeInfo to() @safe nothrow pure const { 1574 return typeid(To); 1575 } 1576 } 1577 1578 /** 1579 Check whether convertor is able to convert from. 1580 1581 Params: 1582 from = the type info of component that could potentially be converted by convertor. 1583 Returns: 1584 true if it is able to convert from, or false otherwise. 1585 **/ 1586 bool convertsFrom(const TypeInfo from) const { 1587 return this.from is from; 1588 } 1589 1590 /** 1591 ditto 1592 **/ 1593 bool convertsFrom(in Object from) const { 1594 return this.convertsTo(from.identify); 1595 } 1596 1597 /** 1598 Check whether convertor is able to convert to. 1599 1600 Params: 1601 to = type info of component that convertor could potentially convert to. 1602 1603 Returns: 1604 true if it is able to convert to, false otherwise. 1605 **/ 1606 bool convertsTo(const TypeInfo to) const { 1607 return this.to is to; 1608 } 1609 1610 /** 1611 ditto 1612 **/ 1613 bool convertsTo(in Object to) const { 1614 return this.convertsTo(to.identify); 1615 } 1616 1617 mixin ConvertsMixin; 1618 1619 /** 1620 Convert from component to component. 1621 1622 It will simply pass existing from component as to component 1623 as consequence requested to component should be same as type info 1624 of from component. 1625 1626 Params: 1627 from = original component that is to be converted. 1628 to = destination object that will be constructed out for original one. 1629 allocator = optional allocator that could be used to construct to component. 1630 Throws: 1631 ConvertorException when convertor is not able to convert from, or to component. 1632 Returns: 1633 Resulting converted component. 1634 **/ 1635 Object convert(in Object from, const TypeInfo to, RCIAllocator allocator = theAllocator) const 1636 { 1637 enforce!ConvertorException( 1638 this.convertsFrom(from), 1639 "Cannot transfer contents of a range ", from.identify, " to output range. Expected range of ", typeid(From) 1640 ); 1641 1642 enforce!ConvertorException( 1643 this.convertsTo(to), 1644 "Cannot transfer contents of range to ", to, " expected an array of ", typeid(Element) 1645 ); 1646 1647 To output; 1648 1649 From source = from.unwrap!From; 1650 1651 debug(trace) trace("Converting from associative array of type ", from.identify, " to associative array of ", to); 1652 foreach (key, value; source) { 1653 auto keyTemporary = key.stored(from, this); 1654 auto valueTemporary = value.stored(from, this); 1655 1656 output[ 1657 this.keyConvertor.convert(keyTemporary, typeid(ToKey), allocator).unpack!ToKey(allocator) 1658 ] = this.elementConvertor.convert(valueTemporary, typeid(ToElement), allocator).unpack!ToElement(allocator); 1659 } 1660 1661 return output.make(allocator); 1662 } 1663 1664 /** 1665 Does not destruct anything since it is not allocating anything. 1666 1667 Params: 1668 converted = component that should be destroyed. 1669 allocator = allocator used to allocate converted component. 1670 **/ 1671 void destruct(const TypeInfo from, ref Object converted, RCIAllocator allocator = theAllocator) const { 1672 enforce!ConvertorException(this.destroys(from, converted), text( 1673 "Cannot destroy ", converted.identify, " which was not converted from ", from, ".", 1674 " Expected destroyable type of ", this.to, " from origin of ", this.from 1675 )); 1676 1677 To destroyable = converted.unpack!To(allocator); 1678 1679 debug(trace) trace("Destroying keys and values of associative array of ", converted.identify, " converted from ", from); 1680 foreach (key, element; destroyable) { 1681 auto keyTemporary = key.stored(from, this); 1682 auto elementTemporary = element.stored(from, this); 1683 1684 this.keyConvertor.destruct(from, keyTemporary, allocator); 1685 this.elementConvertor.destruct(from, elementTemporary, allocator); 1686 } 1687 } 1688 1689 mixin EqualToHashToStringOpCmpMixin!(); 1690 } 1691 } 1692 1693 /** 1694 A convertor that extracts data from a std.variant.VariantN type. 1695 **/ 1696 @component 1697 class VariantConvertor(Variant: VariantN!(maxDataSize, Types), size_t maxDataSize, Types...) : Convertor { 1698 1699 mixin FromMixin!Variant; 1700 mixin ToMixin!Types; 1701 mixin ConvertsFromToMixin DefaultConvertsImplementation; 1702 mixin EqualToHashToStringOpCmpMixin; 1703 1704 bool converts(in Object from, const TypeInfo to) @safe const nothrow { 1705 if (this.DefaultConvertsImplementation.converts(from, to)) { 1706 return from.unwrap!Variant.type is to; 1707 } 1708 1709 return false; 1710 } 1711 1712 bool converts(in Object from, in Object to) @safe const nothrow { 1713 return this.converts(from, to.identify); 1714 } 1715 1716 /** 1717 Convert from component to component. 1718 1719 It will simply pass existing from component as to component 1720 as consequence requested to component should be same as type info 1721 of from component. 1722 1723 Params: 1724 from = original component that is to be converted. 1725 to = destination object that will be constructed out for original one. 1726 allocator = optional allocator that could be used to construct to component. 1727 Throws: 1728 ConvertorException when convertor is not able to convert from, or to component. 1729 Returns: 1730 Resulting converted component. 1731 **/ 1732 Object convert(in Object from, const TypeInfo to, RCIAllocator allocator = theAllocator) const 1733 { 1734 enforce!ConvertorException(this.converts(from, to), text( 1735 "Cannot convert component ", from.identify, " to ", to, " expected original component of ", this.from, " and destination of ", this.to 1736 )); 1737 1738 static foreach (Type; Types) { 1739 if (to is typeid(Type)) { 1740 return from.unwrap!Variant.get!Type.pack(from, this, allocator); 1741 } 1742 } 1743 1744 assert(false, "Code flow should never get here."); 1745 } 1746 1747 /** 1748 Does not destruct anything since it is not allocating anything. 1749 1750 Params: 1751 converted = component that should be destroyed. 1752 allocator = allocator used to allocate converted component. 1753 **/ 1754 void destruct(const TypeInfo from, ref Object converted, RCIAllocator allocator = theAllocator) const { 1755 enforce!ConvertorException(this.destroys(from, converted), text( 1756 "Cannot destroy component ", to.identify, ". Passed component isn't converted by this convertor" 1757 )); 1758 1759 static foreach (Type; Types) { 1760 if (converted.identify is typeid(Type)) { 1761 converted.unpack!Type(allocator); 1762 } 1763 } 1764 } 1765 } 1766 1767 /** 1768 Convert from into component of type To using a convertor for this. 1769 1770 Params: 1771 convertor = the convertor used to convert from component into To 1772 from = original component that is to be converted 1773 allocator = optional allocator used to allocate memory for converted component 1774 1775 Returns: 1776 To converted component 1777 **/ 1778 To convert(To, From)(Convertor convertor, From from, RCIAllocator allocator = theAllocator) { 1779 return convertor.convert(from.stored, typeid(To), allocator).unpack!To; 1780 }