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.core.convertor; 31 32 import aermicioi.aedi; 33 import aermicioi.aedi_property_reader.core.exception : ConvertorException; 34 import aermicioi.aedi.storage.wrapper; 35 import std.meta; 36 import std.conv; 37 import std.experimental.allocator; 38 import std.exception : enforce; 39 import aermicioi.aedi_property_reader.core.accessor; 40 import aermicioi.aedi_property_reader.core.setter; 41 import aermicioi.aedi_property_reader.core.inspector; 42 import aermicioi.aedi_property_reader.core.type_guesser; 43 import aermicioi.aedi_property_reader.core.traits; 44 import aermicioi.aedi_property_reader.core.placeholder; 45 import std.algorithm; 46 import std.array; 47 import std.traits; 48 import std.experimental.logger; 49 import taggedalgebraic : TaggedAlgebraic; 50 51 /** 52 Functional convertor that is taking a component of type From and converts it into a component of type To. 53 54 Params: 55 from = original component that is converted. 56 to = destination component that is constructed. 57 allocator = optional allocator used to allocate required memory during conversion. 58 **/ 59 alias FunctionalConvertor(To, From) = void function(in From, ref To, RCIAllocator allocator = theAllocator); 60 61 /** 62 ditto 63 **/ 64 alias DelegateConvertor(To, From) = void delegate(in From, ref To, RCIAllocator allocator = theAllocator); 65 66 /** 67 Check wether T symbol is a functional convertor. 68 69 Params: 70 T = symbol to be tested. 71 72 Returns: 73 Symbols: Yes -> whether it is or not, From -> type of original component that function is accepting, To -> destination type that function is converting. 74 **/ 75 template isConvertor(alias T) 76 if (isSomeFunction!T) { 77 static if ( 78 is(typeof(&T) : R function(in Y, ref X, RCIAllocator), Y, X, R) || 79 is(typeof(&T) : R delegate(in Y, ref X, RCIAllocator), Y, X, R) || 80 is(typeof(T) : R function(in Y, ref X, RCIAllocator), Y, X, R) || 81 is(typeof(T) : R delegate(in Y, ref X, RCIAllocator), Y, X, R) 82 ) { 83 enum bool Yes = true; 84 85 alias To = X; 86 alias From = Y; 87 } else { 88 89 enum bool Yes = false; 90 } 91 } 92 93 /** 94 Check whether symbol T is a functional convertor from type From to type To. 95 96 Params: 97 T = symbol to test 98 To = destination type 99 From = original type 100 101 Returns: 102 Yes -> whether it is or not, 103 From -> type of original component that function is accepting, 104 To -> destination type that function is converting. 105 **/ 106 template isConvertor(alias T, To, From) { 107 static if (isConvertor!T.Yes && is(isConvertor!T.To == To) && is(isConvertor!T.From == From)) { 108 alias isConvertor = isConvertor!T; 109 } else { 110 enum Yes = false; 111 } 112 } 113 114 /** 115 Test whether template T can be instantiated as a functional convertor from From to To. 116 117 Params: 118 T = template that is to be instantiated 119 To = destination type 120 From = original type 121 Returns: 122 Yes -> whether it is possible to instantiate template as functional convertor 123 Convertor -> resulting functional convertor 124 Info -> data returned by isConvertor 125 **/ 126 template maybeConvertor(alias T, To, From) { 127 static if (is(typeof(isConvertor!(T!(To, From)))) && isConvertor!(T!(To, From)).Yes) { 128 enum Yes = true; 129 alias Convertor = T!(To, From); 130 alias Info = isConvertor!Convertor; 131 } else { 132 enum Yes = false; 133 } 134 } 135 136 /** 137 Functional destructor responsible for destroying components of To type. 138 139 Params: 140 to = the component that is to be destroyed. 141 allocator = the allocator originally used to allocate all memory for To component 142 **/ 143 alias FunctionalDestructor(To) = void function (ref To, RCIAllocator = theAllocator); 144 145 /** 146 ditto 147 **/ 148 alias DelegateDestructor(To) = void delegate (ref To, RCIAllocator = theAllocator); 149 150 /** 151 Check whether symbol T is a functional destructor. 152 153 Params: 154 T = symbol to be tested. 155 Returns: 156 Yes = whether it is functional destructor or not 157 To = the type of accepted components to be destructed 158 **/ 159 template isDestructor(alias T) { 160 static if ( 161 is(typeof(&T) : R function (ref X, RCIAllocator = theAllocator), X, R) || 162 is(typeof(&T) : R delegate (ref X, RCIAllocator = theAllocator), X, R) || 163 is(typeof(T) : R function (ref X, RCIAllocator = theAllocator), X, R) || 164 is(typeof(T) : R delegate (ref X, RCIAllocator = theAllocator), X, R) 165 ) { 166 enum bool Yes = true; 167 168 alias To = X; 169 } else { 170 171 enum bool Yes = false; 172 } 173 } 174 175 /** 176 Check if symbol T is a functional destructor for component of type To 177 178 Params: 179 T = symbol that is tested 180 To = the component that functional destructor may accept 181 182 Returns: 183 Yes = whether it is functional destructor or not 184 To = the type of accepted components to be destructed 185 **/ 186 template isDestructor(T, To) { 187 static if (isDestructor!T.Yes && is(isDestructor!T.To == To)) { 188 alias isDestructor = isDestructor!T; 189 } else { 190 enum Yes = false; 191 } 192 } 193 194 /** 195 Test if T template's instantiantiation with type To is a functional destructor for component of type To. 196 197 Params: 198 T = template that is to be tested. 199 To = the type of component that T instantiation should manage destruction. 200 201 Returns: 202 Yes -> whether it is possible to instantiate a functional destructor for To or not 203 Destructor -> the instantiated functional destructor 204 Info -> information from isDestructor check 205 **/ 206 template maybeDestructor(alias T, To) { 207 static if (isDestructor!(T!To).Yes) { 208 enum Yes = true; 209 alias Destructor = T!(To); 210 alias Info = isDestructor!Destructor; 211 } else { 212 enum Yes = false; 213 } 214 } 215 216 /** 217 Interface for components that are able to convert from one erased type to another erased type. 218 **/ 219 interface Convertor { 220 @property { 221 222 /** 223 Get the type info of component that convertor can convert from. 224 225 Get the type info of component that convertor can convert from. 226 The method is returning the default type that it is able to convert, 227 though it is not necessarily limited to this type only. More generalistic 228 checks should be done by convertsFrom method. 229 230 Returns: 231 type info of component that convertor is able to convert. 232 **/ 233 TypeInfo from() const nothrow; 234 235 /** 236 Get the type info of component that convertor is able to convert to. 237 238 Get the type info of component that convertor is able to convert to. 239 The method is returning the default type that is able to convert, 240 though it is not necessarily limited to this type only. More generalistic 241 checks should be done by convertsTo method. 242 243 Returns: 244 type info of component that can be converted to. 245 **/ 246 TypeInfo to() const nothrow; 247 } 248 249 /** 250 Check whether convertor is able to convert from. 251 252 Check whether convertor is able to convert from. 253 The intent of method is to implement customized type checking 254 is not limited immediatly to supported default from component. 255 256 Params: 257 from = the type info of component that could potentially be converted by convertor. 258 Returns: 259 true if it is able to convert from, or false otherwise. 260 **/ 261 bool convertsFrom(TypeInfo from) const nothrow; 262 263 /** 264 Check whether convertor is able to convert from. 265 266 Check whether convertor is able to convert from. 267 The method will try to extract type info out of from 268 object and use for subsequent type checking. 269 The intent of method is to implement customized type checking 270 is not limited immediatly to supported default from component. 271 272 Params: 273 from = the type info of component that could potentially be converted by convertor. 274 Returns: 275 true if it is able to convert from, or false otherwise. 276 **/ 277 bool convertsFrom(in Object from) const nothrow; 278 279 /** 280 Check whether convertor is able to convert to. 281 282 Check whether convertor is able to convert to. 283 The intent of the method is to implement customized type checking 284 that is not limited immediatly to supported default to component. 285 286 Params: 287 to = type info of component that convertor could potentially convert to. 288 289 Returns: 290 true if it is able to convert to, false otherwise. 291 **/ 292 bool convertsTo(TypeInfo to) const nothrow; 293 294 /** 295 Check whether convertor is able to convert to. 296 297 Check whether convertor is able to convert to. 298 The method will try to extract type info out of to object and use 299 for subsequent type checking. 300 The intent of the method is to implement customized type checking 301 that is not limited immediatly to supported default to component. 302 303 Params: 304 to = type info of component that convertor could potentially convert to. 305 306 Returns: 307 true if it is able to convert to, false otherwise. 308 **/ 309 bool convertsTo(in Object to) const nothrow; 310 311 /** 312 Convert from component to component. 313 314 Params: 315 from = original component that is to be converted. 316 to = destination object that will be constructed out for original one. 317 allocator = optional allocator that could be used to construct to component. 318 Throws: 319 ConvertorException when there is a converting error 320 InvalidArgumentException when arguments passed are not of right type or state 321 Returns: 322 Resulting converted component. 323 **/ 324 Object convert(in Object from, TypeInfo to, RCIAllocator allocator = theAllocator); 325 326 /** 327 Destroy component created using this convertor. 328 329 Destroy component created using this convertor. 330 Since convertor could potentially allocate memory for 331 converted component, only itself is containing history of allocation, 332 and therefore it is responsible as well to destroy and free allocated 333 memory with allocator. 334 335 Params: 336 converted = component that should be destroyed. 337 allocator = allocator used to allocate converted component. 338 **/ 339 void destruct(ref Object converted, RCIAllocator allocator = theAllocator); 340 } 341 342 /** 343 A convertor that is using functional convertor and destructor for conversion logic. 344 **/ 345 class CallbackConvertor(alias convertor, alias destructor) : Convertor 346 if (isConvertor!convertor.Yes && isDestructor!destructor.Yes) { 347 348 private { 349 alias Info = isConvertor!convertor; 350 } 351 352 public { 353 354 @property { 355 /** 356 Get the type info of component that convertor can convert from. 357 358 Get the type info of component that convertor can convert from. 359 The method is returning the default type that it is able to convert, 360 though it is not necessarily limited to this type only. More generalistic 361 checks should be done by convertsFrom method. 362 363 Returns: 364 type info of component that convertor is able to convert. 365 **/ 366 TypeInfo from() @safe nothrow pure const { 367 return typeid(Info.From); 368 } 369 370 /** 371 Get the type info of component that convertor is able to convert to. 372 373 Get the type info of component that convertor is able to convert to. 374 The method is returning the default type that is able to convert, 375 though it is not necessarily limited to this type only. More generalistic 376 checks should be done by convertsTo method. 377 378 Returns: 379 type info of component that can be converted to. 380 **/ 381 TypeInfo to() @safe nothrow pure const { 382 return typeid(Info.To); 383 } 384 } 385 386 /** 387 Check whether convertor is able to convert from. 388 389 Params: 390 from = the type info of component that could potentially be converted by convertor. 391 Returns: 392 true if it is able to convert from, or false otherwise. 393 **/ 394 bool convertsFrom(TypeInfo from) const nothrow { 395 return typeid(Info.From) is from; 396 } 397 398 /** 399 ditto 400 **/ 401 bool convertsFrom(in Object from) const nothrow { 402 return this.convertsFrom(from.identify); 403 } 404 405 /** 406 Check whether convertor is able to convert to. 407 408 Params: 409 to = type info of component that convertor could potentially convert to. 410 411 Returns: 412 true if it is able to convert to, false otherwise. 413 **/ 414 bool convertsTo(TypeInfo to) const nothrow { 415 return typeid(Info.To) is to; 416 } 417 418 /** 419 ditto 420 **/ 421 bool convertsTo(in Object to) const nothrow { 422 return this.convertsTo(to.identify); 423 } 424 425 /** 426 Convert from component to component. 427 428 Convert from component to component. 429 In case when functional convertor's from argument is rooted in Object 430 class, the convertor will attempt to downcast passed component to 431 it's rightfull type. If not, the convertor will attempt to downcast 432 from component to placeholding object of value that is accepted by functional 433 convertor. 434 For converted to component, in case when it is rooted in Object class 435 nothing special is performed, while otherwise it will be wrapped into placeholding 436 object allocated by allocator that will be returned to callee. 437 438 Params: 439 from = original component that is to be converted. 440 to = destination object that will be constructed out for original one. 441 allocator = optional allocator that could be used to construct to component. 442 Throws: 443 ConvertorException when convertor is not able to convert from, or to component. 444 Returns: 445 Resulting converted component. 446 **/ 447 Object convert(in Object from, TypeInfo to, RCIAllocator allocator = theAllocator) 448 { 449 enforce!ConvertorException(this.convertsTo(to), text(to, " is not supported by convertor expected ", typeid(Info.To))); 450 enforce!ConvertorException(this.convertsFrom(from), text(from.identify, " is not supported by convertor expected ", typeid(Info.From))); 451 452 Info.From naked; 453 454 static if (is(From : Object)) { 455 naked = cast(From) from; 456 457 if (naked is null) { 458 throw new ConvertorException(text("Cannot convert ", from.classinfo, " only supported ", this.from)); 459 } 460 } else { 461 462 auto wrapper = (cast(Placeholder!(Info.From)) from); 463 464 if (wrapper is null) { 465 throw new ConvertorException(text("Cannot convert ", from.identify, " only supported ", this.from)); 466 } 467 468 naked = wrapper.value; 469 } 470 471 472 static if (is(Info.To : Object)) { 473 Info.To placeholder = make!(Info.To); 474 475 convertor(naked, placeholder, allocator); 476 } else { 477 PlaceholderImpl!(Info.To) placeholder = allocator.make!(PlaceholderImpl!(Info.To))(Info.To.init); 478 479 convertor(naked, placeholder.value, allocator); 480 } 481 482 return placeholder; 483 } 484 485 /** 486 Destroy component created using this convertor. 487 488 Destroy component created using this convertor. 489 The method will attempt to downcast converted component 490 to type supported by functional destructor when the type is rooted in 491 Object class, otherwise it will attempt to downcast converted to placeholding 492 object of value supported by functional destructor. In latest case, the 493 method will dispose out of placeholding object using passed allocator. 494 495 Params: 496 converted = component that should be destroyed. 497 allocator = allocator used to allocate converted component. 498 **/ 499 void destruct(ref Object converted, RCIAllocator allocator = theAllocator) { 500 static if (is(Info.To : Object)) { 501 502 destructor(converted, allocator); 503 } else { 504 auto container = cast(Placeholder!(Info.To)) converted; 505 506 destructor(container.value, allocator); 507 allocator.dispose(converted); 508 } 509 } 510 } 511 } 512 513 /** 514 A convertor that is providing additional type information for converted tagged types. 515 516 A convertor that is providing additional type information for converted tagged types. 517 In case when wrong convertor is passed to tagged convertor, any type conversion 518 and information capabilities are inhibited. 519 **/ 520 class TaggedConvertor(Tagged : TaggedAlgebraic!Union, Union) : Convertor { 521 522 private { 523 Convertor convertor_; 524 } 525 526 public { 527 this(Convertor convertor) { 528 this.convertor = convertor; 529 } 530 531 @property { 532 /** 533 Set convertor 534 535 Params: 536 convertor = convertor accepting a tagged union that will be decorated with addional type information. 537 538 Returns: 539 typeof(this) 540 **/ 541 typeof(this) convertor(Convertor convertor) @safe nothrow pure { 542 this.convertor_ = convertor; 543 544 return this; 545 } 546 547 /** 548 Get convertor 549 550 Returns: 551 Convertor 552 **/ 553 inout(Convertor) convertor() @safe nothrow pure inout { 554 return this.convertor_; 555 } 556 557 /** 558 Get the type info of component that convertor can convert from. 559 560 Get the type info of component that convertor can convert from. 561 The method is returning the default type that it is able to convert, 562 though it is not necessarily limited to this type only. More generalistic 563 checks should be done by convertsFrom method. 564 565 Returns: 566 type info of component that convertor is able to convert. 567 **/ 568 TypeInfo from() nothrow const { 569 return this.convertor.from is typeid(Tagged) ? typeid(Tagged) : typeid(void); 570 } 571 572 /** 573 Get the type info of component that convertor is able to convert to. 574 575 Get the type info of component that convertor is able to convert to. 576 The method is returning the default type that is able to convert, 577 though it is not necessarily limited to this type only. More generalistic 578 checks should be done by convertsTo method. 579 580 Returns: 581 type info of component that can be converted to. 582 **/ 583 TypeInfo to() nothrow const { 584 return this.convertor.to; 585 } 586 } 587 588 /** 589 Check whether convertor is able to convert from. 590 591 Params: 592 from = the type info of component that could potentially be converted by convertor. 593 Returns: 594 true if it is able to convert from, or false otherwise. 595 **/ 596 bool convertsFrom(TypeInfo from) const nothrow { 597 if (from is typeid(void)) { 598 return false; 599 } 600 601 if (this.convertor.convertsFrom(from)) { 602 return true; 603 } 604 605 static foreach (T; Fields!Union) { 606 if (from is typeid(T)) { 607 return true; 608 } 609 } 610 611 return false; 612 } 613 614 /** 615 ditto 616 **/ 617 bool convertsFrom(in Object from) const nothrow { 618 return this.convertsFrom(from.identify); 619 } 620 621 /** 622 Check whether convertor is able to convert to. 623 624 Params: 625 to = type info of component that convertor could potentially convert to. 626 627 Returns: 628 true if it is able to convert to, false otherwise. 629 **/ 630 bool convertsTo(TypeInfo to) const nothrow { 631 return this.convertor.convertsTo(to); 632 } 633 634 /** 635 ditto 636 **/ 637 bool convertsTo(in Object to) const nothrow { 638 return this.convertsTo(to.identify); 639 } 640 641 /** 642 Convert from component to component. 643 644 Convert from component to component. 645 In case when functional convertor's from argument is rooted in Object 646 class, the convertor will attempt to downcast passed component to 647 it's rightfull type. If not, the convertor will attempt to downcast 648 from component to placeholding object of value that is accepted by functional 649 convertor. 650 For converted to component, in case when it is rooted in Object class 651 nothing special is performed, while otherwise it will be wrapped into placeholding 652 object allocated by allocator that will be returned to callee. 653 654 Params: 655 from = original component that is to be converted. 656 to = destination object that will be constructed out for original one. 657 allocator = optional allocator that could be used to construct to component. 658 Throws: 659 ConvertorException when convertor is not able to convert from, or to component. 660 Returns: 661 Resulting converted component. 662 **/ 663 Object convert(in Object from, TypeInfo to, RCIAllocator allocator = theAllocator) 664 { 665 return this.convertor.convert(from, to, allocator); 666 } 667 668 /** 669 Destroy component created using this convertor. 670 671 Destroy component created using this convertor. 672 The method will attempt to downcast converted component 673 to type supported by functional destructor when the type is rooted in 674 Object class, otherwise it will attempt to downcast converted to placeholding 675 object of value supported by functional destructor. In latest case, the 676 method will dispose out of placeholding object using passed allocator. 677 678 Params: 679 converted = component that should be destroyed. 680 allocator = allocator used to allocate converted component. 681 **/ 682 void destruct(ref Object converted, RCIAllocator allocator = theAllocator) { 683 return this.convertor.destruct(converted, allocator); 684 } 685 } 686 } 687 688 /** 689 Interface for convertors that could rely on another convertors to convert a value. 690 **/ 691 interface CombinedConvertor : Convertor { 692 693 /** 694 Set used convertors 695 696 Params: 697 convertors = list of convertors to be used. 698 699 Returns: 700 typeof(this) 701 **/ 702 typeof(this) convertors(Convertor[] convertors) @safe nothrow; 703 704 /** 705 ditto 706 **/ 707 final T convertors(this T)(Convertor convertors...) @safe nothrow { 708 return this.convertors(convertors.dup); 709 } 710 711 /** 712 Add a convertor to existing list 713 714 Params: 715 convertor = convertor to be added to 716 717 Returns: 718 typeof(this) 719 **/ 720 typeof(this) add(Convertor convertor) @safe nothrow; 721 722 /** 723 Remove a convertor from existing list 724 725 Params: 726 convertor = convertor to be removed 727 728 Returns: 729 typeof(this) 730 **/ 731 typeof(this) remove(Convertor convertor) @safe nothrow; 732 } 733 734 /** 735 A convertor that is delegating converting task to a set of child convertors. 736 **/ 737 class CombinedConvertorImpl : CombinedConvertor { 738 import std.algorithm; 739 740 private { 741 Convertor[] convertors_; 742 } 743 744 public { 745 746 /** 747 Default constructor for AggregateConvertor 748 **/ 749 this(Convertor[] convertors...) { 750 this.convertors = convertors.dup; 751 } 752 753 /** 754 Set convertors 755 756 Params: 757 convertors = convertors used to convert from one type to another 758 759 Returns: 760 typeof(this) 761 **/ 762 typeof(this) convertors(Convertor[] convertors) @safe nothrow pure { 763 this.convertors_ = convertors; 764 765 return this; 766 } 767 768 /** 769 Get convertors 770 771 Returns: 772 Convertor[] 773 **/ 774 inout(Convertor[]) convertors() @safe nothrow pure inout { 775 return this.convertors_; 776 } 777 778 /** 779 Add a convertor to existing list 780 781 Params: 782 convertor = convertor to be added to 783 784 Returns: 785 typeof(this) 786 **/ 787 typeof(this) add(Convertor convertor) @safe { 788 this.convertors_ ~= convertor; 789 790 return this; 791 } 792 793 /** 794 Remove a convertor from existing list 795 796 Params: 797 convertor = convertor to be removed 798 799 Returns: 800 typeof(this) 801 **/ 802 typeof(this) remove(Convertor convertor) @trusted nothrow { 803 import std.algorithm : remove, countUntil; 804 import std.array : array; 805 806 try { 807 808 this.convertors_ = this.convertors_.remove(this.convertors.countUntil!(c => c == convertor)); 809 } catch (Exception e) { 810 assert(false, text("countUntil threw an exception: ", e)); 811 } 812 813 return this; 814 } 815 816 @property { 817 /** 818 Get the type info of component that convertor can convert from. 819 820 Get the type info of component that convertor can convert from. 821 The method is returning the default type that it is able to convert, 822 though it is not necessarily limited to this type only. More generalistic 823 checks should be done by convertsFrom method. 824 825 Returns: 826 type info of component that convertor is able to convert. 827 **/ 828 TypeInfo from() @safe nothrow pure const { 829 return typeid(void); 830 } 831 832 /** 833 Get the type info of component that convertor is able to convert to. 834 835 Get the type info of component that convertor is able to convert to. 836 The method is returning the default type that is able to convert, 837 though it is not necessarily limited to this type only. More generalistic 838 checks should be done by convertsTo method. 839 840 Returns: 841 type info of component that can be converted to. 842 **/ 843 TypeInfo to() @safe nothrow pure const { 844 return typeid(void); 845 } 846 } 847 848 /** 849 Check whether convertor is able to convert from. 850 851 Params: 852 from = the type info of component that could potentially be converted by convertor. 853 Returns: 854 true if it is able to convert from, or false otherwise. 855 **/ 856 bool convertsFrom(TypeInfo from) const { 857 return this.convertors.canFind!(c => c.convertsFrom(from)); 858 } 859 860 /** 861 ditto 862 **/ 863 bool convertsFrom(in Object from) const { 864 return this.convertors.canFind!(c => c.convertsFrom(from)); 865 } 866 867 /** 868 Check whether convertor is able to convert to. 869 870 Params: 871 to = type info of component that convertor could potentially convert to. 872 873 Returns: 874 true if it is able to convert to, false otherwise. 875 **/ 876 bool convertsTo(TypeInfo to) const { 877 return this.convertors.canFind!(c => c.convertsTo(to)); 878 } 879 880 /** 881 ditto 882 **/ 883 bool convertsTo(in Object to) const { 884 return this.convertors.canFind!(c => c.convertsTo(to)); 885 } 886 887 /** 888 Convert from component to component. 889 890 Finds a right convertor from component to component and uses it 891 to execute conversion from component to component. 892 893 Params: 894 from = original component that is to be converted. 895 to = destination object that will be constructed out for original one. 896 allocator = optional allocator that could be used to construct to component. 897 Throws: 898 ConvertorException when convertor is not able to convert from, or to component. 899 Returns: 900 Resulting converted component. 901 **/ 902 Object convert(in Object from, TypeInfo to, RCIAllocator allocator = theAllocator) 903 { 904 auto convertors = this.convertors.find!(c => c.convertsFrom(from) && c.convertsTo(to)); 905 906 if (!convertors.empty) { 907 return convertors[0].convert(from, to, allocator); 908 } 909 910 throw new ConvertorException(text("Could not convert ", typeid(from), " to type ", to)); 911 } 912 913 /** 914 Destroy component created using this convertor. 915 916 Find a suitable convertor for destruction and use it to execute destruction. 917 918 Params: 919 converted = component that should be destroyed. 920 allocator = allocator used to allocate converted component. 921 **/ 922 void destruct(ref Object converted, RCIAllocator allocator = theAllocator) { 923 auto convertors = this.convertors.find!(c => c.convertsTo(converted)); 924 925 if (convertors.empty) { 926 throw new ConvertorException(text("Could not destroy ", converted)); 927 } 928 929 convertors[0].destruct(converted, allocator); 930 } 931 } 932 } 933 934 /** 935 A convertor that simply doesn't do any conversion and returns existing object 936 **/ 937 class NoOpConvertor : Convertor { 938 import std.algorithm; 939 940 public { 941 @property { 942 /** 943 Get the type info of component that convertor can convert from. 944 945 Get the type info of component that convertor can convert from. 946 The method is returning the default type that it is able to convert, 947 though it is not necessarily limited to this type only. More generalistic 948 checks should be done by convertsFrom method. 949 950 Returns: 951 type info of component that convertor is able to convert. 952 **/ 953 TypeInfo from() @safe nothrow pure const { 954 return typeid(Object); 955 } 956 957 /** 958 Get the type info of component that convertor is able to convert to. 959 960 Get the type info of component that convertor is able to convert to. 961 The method is returning the default type that is able to convert, 962 though it is not necessarily limited to this type only. More generalistic 963 checks should be done by convertsTo method. 964 965 Returns: 966 type info of component that can be converted to. 967 **/ 968 TypeInfo to() @safe nothrow pure const { 969 return typeid(Object); 970 } 971 } 972 973 /** 974 Check whether convertor is able to convert from. 975 976 Params: 977 from = the type info of component that could potentially be converted by convertor. 978 Returns: 979 true if it is able to convert from, or false otherwise. 980 **/ 981 bool convertsFrom(TypeInfo from) const { 982 return this.from is from; 983 } 984 985 /** 986 ditto 987 **/ 988 bool convertsFrom(in Object from) const { 989 return this.convertsTo(typeid(from)); 990 } 991 992 /** 993 Check whether convertor is able to convert to. 994 995 Params: 996 to = type info of component that convertor could potentially convert to. 997 998 Returns: 999 true if it is able to convert to, false otherwise. 1000 **/ 1001 bool convertsTo(TypeInfo to) const { 1002 return this.to is from; 1003 } 1004 1005 /** 1006 ditto 1007 **/ 1008 bool convertsTo(in Object to) const { 1009 return this.convertsTo(typeid(to)); 1010 } 1011 1012 /** 1013 Convert from component to component. 1014 1015 It will simply pass existing from component as to component 1016 as consequence requested to component should be same as type info 1017 of from component. 1018 1019 Params: 1020 from = original component that is to be converted. 1021 to = destination object that will be constructed out for original one. 1022 allocator = optional allocator that could be used to construct to component. 1023 Throws: 1024 ConvertorException when convertor is not able to convert from, or to component. 1025 Returns: 1026 Resulting converted component. 1027 **/ 1028 Object convert(in Object from, TypeInfo to, RCIAllocator allocator = theAllocator) 1029 { 1030 enforce!ConvertorException( 1031 from.identify is to, 1032 text("Cannot do no op when expected ", to, " type is not of same type as original from ", from.identify, " type") 1033 ); 1034 1035 return cast() from; 1036 } 1037 1038 /** 1039 Does not destruct anything since it is not allocating anything. 1040 1041 Params: 1042 converted = component that should be destroyed. 1043 allocator = allocator used to allocate converted component. 1044 **/ 1045 void destruct(ref Object converted, RCIAllocator allocator = theAllocator) { 1046 1047 } 1048 } 1049 } 1050 1051 /** 1052 Convert from into component of type To using a convertor for this. 1053 1054 Params: 1055 convertor = the convertor used to convert from component into To 1056 from = original component that is to be converted 1057 allocator = optional allocator used to allocate memory for converted component 1058 1059 Returns: 1060 To converted component 1061 **/ 1062 To convert(To, From)(Convertor convertor, From from, RCIAllocator allocator = theAllocator) { 1063 import std.typecons : scoped; 1064 static if (is(From : Object)) { 1065 1066 Object converted = convertor.convert(from, typeid(To), allocator); 1067 } else { 1068 1069 Object converted = convertor.convert(scoped!(PlaceholderImpl!From)(from), typeid(To), allocator); 1070 } 1071 1072 static if (is(To : Object)) { 1073 1074 return cast(To) converted; 1075 } else { 1076 1077 scope(exit) allocator.dispose(converted); 1078 return (cast(Placeholder!To) converted).value; 1079 } 1080 } 1081 1082 /** 1083 A callback convertor factory advised with functional convertor and destructor. 1084 1085 Params: 1086 convertor = functional convertor used for callback convertor 1087 destructor = functional destructor used for callback convertor 1088 1089 Returns: 1090 AdvisedConvertor(To, From) template ready to instantiate a callback convertor for To, and From types using convertor and destructor. 1091 **/ 1092 template AdvisedConvertor(alias convertor, alias destructor) { 1093 import std.traits; 1094 1095 template CheckMessage(To, From) { 1096 alias ConvertorInfo = maybeConvertor!(convertor, To, From); 1097 alias DestructorInfo = maybeDestructor!(destructor, To); 1098 1099 import std.traits : fullyQualifiedName; 1100 enum CheckMessage = text( 1101 "Cannot convert type ", 1102 fullyQualifiedName!From, 1103 " to ", 1104 fullyQualifiedName!To, 1105 " when:\n ", 1106 fullyQualifiedName!convertor, 1107 " implements convertor ", 1108 ConvertorInfo.Yes, 1109 "\n ", 1110 fullyQualifiedName!destructor, 1111 " implements destructor ", 1112 DestructorInfo.Yes 1113 ); 1114 1115 /// Enable it for what reason creation of convertor fails, if it is not due to type constraints. 1116 debug (AdvisedConvertorDebug) { 1117 alias Debug = ConvertorImpl!(ConvertorInfo, DestructorInfo); 1118 } 1119 } 1120 1121 template ConvertorImpl(alias ConvertorInfo, alias DestructorInfo) { 1122 1123 alias ConvertorImpl = () { 1124 auto convertor = new CallbackConvertor!(ConvertorInfo.Convertor, DestructorInfo.Destructor)(); 1125 1126 static if (is(From : TaggedAlgebraic!Union, Union)) { 1127 return new TaggedConvertor!From(convertor); 1128 } else { 1129 return convertor; 1130 } 1131 }; 1132 } 1133 1134 template AdvisedConvertorImplementation(To, From) { 1135 alias ConvertorInfo = maybeConvertor!(convertor, To, From); 1136 alias DestructorInfo = maybeDestructor!(destructor, To); 1137 1138 static if (ConvertorInfo.Yes && DestructorInfo.Yes) { 1139 1140 alias AdvisedConvertorImplementation = ConvertorImpl!(ConvertorInfo, DestructorInfo); 1141 } else { 1142 1143 static assert(false, CheckMessage!(To, From)); 1144 } 1145 } 1146 } 1147 1148 /** 1149 A composite convertor factory advised with accessor, setter, and inspectors. 1150 1151 Params: 1152 Accessor = accessor used to extract data out of From component 1153 Setter = setter used to set extracted data into To component 1154 FromInspector = inspector used to inspect From components for accessible fields 1155 ToInspector = inspector used to inspect To components for settable fields 1156 Returns: 1157 AdvisedConvertor(To, From) template ready to instantiate a composite convertor for To, and From types using advised accessor, setter, and inspectors. 1158 **/ 1159 template AdvisedConvertor(alias Accessor, alias Setter, alias ToInspector, alias FromInspector) { 1160 1161 enum AccessorCheck(To, From) = (is(typeof(Accessor!From()) : PropertyAccessor!(From, Object))); 1162 enum SetterCheck(To, From) = (is (typeof(Setter!To()) : PropertySetter!(To, Object))); 1163 enum FromInspectorCheck(To, From) = (is (typeof(FromInspector!From()) : Inspector!From)); 1164 enum ToInspectorCheck(To, From) = (is (typeof(ToInspector!To()) : Inspector!To)); 1165 1166 template CheckMessage(To, From) { 1167 import std.traits : fullyQualifiedName; 1168 1169 enum CheckMessage = text( 1170 "Cannot convert type ", 1171 fullyQualifiedName!From, 1172 " to ", 1173 fullyQualifiedName!To, 1174 " when:\n ", 1175 fullyQualifiedName!Accessor, 1176 " is able to access ", fullyQualifiedName!From, " ", 1177 AccessorCheck!(To, From), 1178 ",\n ", 1179 fullyQualifiedName!Setter, 1180 " is able to set ", fullyQualifiedName!To, " ", 1181 SetterCheck!(To, From), 1182 ",\n ", 1183 fullyQualifiedName!FromInspector, 1184 " is able to inspect ", fullyQualifiedName!From, " ", 1185 FromInspectorCheck!(To, From), 1186 ",\n ", 1187 fullyQualifiedName!ToInspector, 1188 " is able to inspect ", fullyQualifiedName!To, " ", 1189 ToInspectorCheck!(To, From) 1190 ); 1191 1192 /// Enable it for what reason creation of convertor fails, if it is not due to type constraints. 1193 debug (AdvisedConvertorDebug) { 1194 alias Debug = AdvisedConvertorImplementation!(To, From); 1195 } 1196 } 1197 1198 template AdvisedConvertorImplementation(To, From) { 1199 1200 static if ( 1201 AccessorCheck!(To, From) && 1202 SetterCheck!(To, From) && 1203 FromInspectorCheck!(To, From) && 1204 ToInspectorCheck!(To, From) 1205 ) { 1206 alias AdvisedConvertorImplementation = (bool conversion = true, bool force = true, bool skip = true) { 1207 import aermicioi.aedi_property_reader.core.mapper : CompositeMapper, CompositeConvertor; 1208 1209 auto convertor = new CompositeConvertor!(To, From)(); 1210 CompositeMapper!(To, From) mapper = new CompositeMapper!(To, From)(); 1211 mapper.fromInspector = FromInspector!From(); 1212 mapper.toInspector = ToInspector!To(); 1213 mapper.accessor = Accessor!From(); 1214 mapper.setter = Setter!To(); 1215 mapper.conversion = conversion; 1216 mapper.force = force; 1217 mapper.skip = skip; 1218 convertor.mapper = mapper; 1219 1220 static if (is(From : TaggedAlgebraic!Union, Union)) { 1221 return new class(convertor) TaggedConvertor!From, CombinedConvertor { 1222 private CompositeConvertor!(To, From) cc; 1223 this(CompositeConvertor!(To, From) c) { 1224 super(c); 1225 this.cc = c; 1226 } 1227 typeof(this) convertors(Convertor[] convertors) @safe nothrow { this.cc.convertors = convertors; return this; } 1228 typeof(this) add(Convertor convertor) @safe nothrow { this.cc.add(convertor); return this; } 1229 typeof(this) remove(Convertor convertor) @safe nothrow { this.cc.remove(convertor); return this; } 1230 }; 1231 } else { 1232 return convertor; 1233 } 1234 }; 1235 } else { 1236 1237 static assert(false, CheckMessage!(To, From)); 1238 } 1239 } 1240 } 1241 1242 template ChainedAdvisedConvertor(AdvisedConvertors...) { 1243 1244 template CheckMessage(To, From) { 1245 import std.traits : fullyQualifiedName; 1246 import aermicioi.util.traits : getMember; 1247 enum CheckMessage = text( 1248 "None of advised convertors are able to convert ", 1249 fullyQualifiedName!From, 1250 " to ", 1251 fullyQualifiedName!To, 1252 " where advised convertors are failing with:\n ", 1253 staticMap!( 1254 ApplyRight!(AliasSeq, "\n"), 1255 staticMap!( 1256 ApplyRight!(Instantiate, To, From), 1257 staticMap!( 1258 ApplyRight!(getMember, "CheckMessage"), 1259 AdvisedConvertors 1260 ) 1261 ) 1262 ) 1263 ); 1264 } 1265 1266 template AdvisedConvertorImplementation(To, From) { 1267 1268 static foreach (AdvisedConvertor; AdvisedConvertors) { 1269 import std.traits; 1270 static if (!is(typeof(Yes)) && is(typeof(AdvisedConvertor.AdvisedConvertorImplementation!(To, From)))) { 1271 enum Yes = true; 1272 alias AdvisedConvertorImplementation = AdvisedConvertor.AdvisedConvertorImplementation!(To, From); 1273 } 1274 } 1275 1276 static if (!is(typeof(Yes))) { 1277 static assert(false, CheckMessage!(To, From)); 1278 } 1279 } 1280 } 1281 1282 public { 1283 enum CompositeAccessorFactory(T) = () => new CompositeAccessor!T; 1284 enum CompositeSetterFactory(T) = () => new CompositeSetter!T; 1285 enum CompositeInspectorFactory(T) = () => new CompositeInspector!T; 1286 } 1287 1288 alias CompositeAdvisedConvertor = AdvisedConvertor!( 1289 CompositeAccessorFactory, 1290 CompositeSetterFactory, 1291 CompositeInspectorFactory, 1292 CompositeInspectorFactory 1293 );