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 moduleaermicioi.aedi_property_reader.core.convertor;
31 32 importaermicioi.aedi;
33 importaermicioi.aedi_property_reader.core.exception : ConvertorException;
34 importaermicioi.aedi.storage.wrapper;
35 importstd.meta;
36 importstd.conv;
37 importstd.experimental.allocator;
38 importstd.exception : enforce;
39 importaermicioi.aedi_property_reader.core.accessor;
40 importaermicioi.aedi_property_reader.core.setter;
41 importaermicioi.aedi_property_reader.core.inspector;
42 importaermicioi.aedi_property_reader.core.type_guesser;
43 importaermicioi.aedi_property_reader.core.traits;
44 importaermicioi.aedi_property_reader.core.placeholder;
45 importstd.algorithm;
46 importstd.array;
47 importstd.traits;
48 importstd.experimental.logger;
49 importtaggedalgebraic : 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 aliasFunctionalConvertor(To, From) = voidfunction(inFrom, refTo, RCIAllocatorallocator = theAllocator);
60 61 /**
62 ditto
63 **/64 aliasDelegateConvertor(To, From) = voiddelegate(inFrom, refTo, RCIAllocatorallocator = 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 templateisConvertor(aliasT)
76 if (isSomeFunction!T) {
77 staticif (
78 is(typeof(&T) : Rfunction(inY, refX, RCIAllocator), Y, X, R) ||
79 is(typeof(&T) : Rdelegate(inY, refX, RCIAllocator), Y, X, R) ||
80 is(typeof(T) : Rfunction(inY, refX, RCIAllocator), Y, X, R) ||
81 is(typeof(T) : Rdelegate(inY, refX, RCIAllocator), Y, X, R)
82 ) {
83 enumboolYes = true;
84 85 aliasTo = X;
86 aliasFrom = Y;
87 } else {
88 89 enumboolYes = 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 templateisConvertor(aliasT, To, From) {
107 staticif (isConvertor!T.Yes && is(isConvertor!T.To == To) && is(isConvertor!T.From == From)) {
108 aliasisConvertor = isConvertor!T;
109 } else {
110 enumYes = 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 templatemaybeConvertor(aliasT, To, From) {
127 staticif (is(typeof(isConvertor!(T!(To, From)))) && isConvertor!(T!(To, From)).Yes) {
128 enumYes = true;
129 aliasConvertor = T!(To, From);
130 aliasInfo = isConvertor!Convertor;
131 } else {
132 enumYes = 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 aliasFunctionalDestructor(To) = voidfunction (refTo, RCIAllocator = theAllocator);
144 145 /**
146 ditto
147 **/148 aliasDelegateDestructor(To) = voiddelegate (refTo, 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 templateisDestructor(aliasT) {
160 staticif (
161 is(typeof(&T) : Rfunction (refX, RCIAllocator = theAllocator), X, R) ||
162 is(typeof(&T) : Rdelegate (refX, RCIAllocator = theAllocator), X, R) ||
163 is(typeof(T) : Rfunction (refX, RCIAllocator = theAllocator), X, R) ||
164 is(typeof(T) : Rdelegate (refX, RCIAllocator = theAllocator), X, R)
165 ) {
166 enumboolYes = true;
167 168 aliasTo = X;
169 } else {
170 171 enumboolYes = 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 templateisDestructor(T, To) {
187 staticif (isDestructor!T.Yes && is(isDestructor!T.To == To)) {
188 aliasisDestructor = isDestructor!T;
189 } else {
190 enumYes = 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 templatemaybeDestructor(aliasT, To) {
207 staticif (isDestructor!(T!To).Yes) {
208 enumYes = true;
209 aliasDestructor = T!(To);
210 aliasInfo = isDestructor!Destructor;
211 } else {
212 enumYes = false;
213 }
214 }
215 216 /**
217 Interface for components that are able to convert from one erased type to another erased type.
218 **/219 interfaceConvertor {
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 TypeInfofrom() constnothrow;
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 TypeInfoto() constnothrow;
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 boolconvertsFrom(TypeInfofrom) constnothrow;
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 boolconvertsFrom(inObjectfrom) constnothrow;
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 boolconvertsTo(TypeInfoto) constnothrow;
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 boolconvertsTo(inObjectto) constnothrow;
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 Objectconvert(inObjectfrom, TypeInfoto, RCIAllocatorallocator = 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 voiddestruct(refObjectconverted, RCIAllocatorallocator = theAllocator);
340 }
341 342 /**
343 A convertor that is using functional convertor and destructor for conversion logic.
344 **/345 classCallbackConvertor(aliasconvertor, aliasdestructor) : Convertor346 if (isConvertor!convertor.Yes && isDestructor!destructor.Yes) {
347 348 private {
349 aliasInfo = 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 TypeInfofrom() @safenothrowpureconst {
367 returntypeid(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 TypeInfoto() @safenothrowpureconst {
382 returntypeid(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 boolconvertsFrom(TypeInfofrom) constnothrow {
395 returntypeid(Info.From) isfrom;
396 }
397 398 /**
399 ditto
400 **/401 boolconvertsFrom(inObjectfrom) constnothrow {
402 returnthis.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 boolconvertsTo(TypeInfoto) constnothrow {
415 returntypeid(Info.To) isto;
416 }
417 418 /**
419 ditto
420 **/421 boolconvertsTo(inObjectto) constnothrow {
422 returnthis.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 Objectconvert(inObjectfrom, TypeInfoto, RCIAllocatorallocator = 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.Fromnaked;
453 454 staticif (is(From : Object)) {
455 naked = cast(From) from;
456 457 if (nakedisnull) {
458 thrownewConvertorException(text("Cannot convert ", from.classinfo, " only supported ", this.from));
459 }
460 } else {
461 462 autowrapper = (cast(Placeholder!(Info.From)) from);
463 464 if (wrapperisnull) {
465 thrownewConvertorException(text("Cannot convert ", from.identify, " only supported ", this.from));
466 }
467 468 naked = wrapper.value;
469 }
470 471 472 staticif (is(Info.To : Object)) {
473 Info.Toplaceholder = 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 returnplaceholder;
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 voiddestruct(refObjectconverted, RCIAllocatorallocator = theAllocator) {
500 staticif (is(Info.To : Object)) {
501 502 destructor(converted, allocator);
503 } else {
504 autocontainer = 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 classTaggedConvertor(Tagged : TaggedAlgebraic!Union, Union) : Convertor {
521 522 private {
523 Convertorconvertor_;
524 }
525 526 public {
527 this(Convertorconvertor) {
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(Convertorconvertor) @safenothrowpure {
542 this.convertor_ = convertor;
543 544 returnthis;
545 }
546 547 /**
548 Get convertor
549 550 Returns:
551 Convertor
552 **/553 inout(Convertor) convertor() @safenothrowpureinout {
554 returnthis.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 TypeInfofrom() nothrowconst {
569 returnthis.convertor.fromistypeid(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 TypeInfoto() nothrowconst {
584 returnthis.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 boolconvertsFrom(TypeInfofrom) constnothrow {
597 if (fromistypeid(void)) {
598 returnfalse;
599 }
600 601 if (this.convertor.convertsFrom(from)) {
602 returntrue;
603 }
604 605 staticforeach (T; Fields!Union) {
606 if (fromistypeid(T)) {
607 returntrue;
608 }
609 }
610 611 returnfalse;
612 }
613 614 /**
615 ditto
616 **/617 boolconvertsFrom(inObjectfrom) constnothrow {
618 returnthis.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 boolconvertsTo(TypeInfoto) constnothrow {
631 returnthis.convertor.convertsTo(to);
632 }
633 634 /**
635 ditto
636 **/637 boolconvertsTo(inObjectto) constnothrow {
638 returnthis.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 Objectconvert(inObjectfrom, TypeInfoto, RCIAllocatorallocator = theAllocator)
664 {
665 returnthis.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 voiddestruct(refObjectconverted, RCIAllocatorallocator = theAllocator) {
683 returnthis.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 interfaceCombinedConvertor : 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) @safenothrow;
703 704 /**
705 ditto
706 **/707 finalTconvertors(thisT)(Convertorconvertors...) @safenothrow {
708 returnthis.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(Convertorconvertor) @safenothrow;
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(Convertorconvertor) @safenothrow;
732 }
733 734 /**
735 A convertor that is delegating converting task to a set of child convertors.
736 **/737 classCombinedConvertorImpl : CombinedConvertor {
738 importstd.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) @safenothrowpure {
763 this.convertors_ = convertors;
764 765 returnthis;
766 }
767 768 /**
769 Get convertors
770 771 Returns:
772 Convertor[]
773 **/774 inout(Convertor[]) convertors() @safenothrowpureinout {
775 returnthis.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(Convertorconvertor) @safe {
788 this.convertors_ ~= convertor;
789 790 returnthis;
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(Convertorconvertor) @trustednothrow {
803 importstd.algorithm : remove, countUntil;
804 importstd.array : array;
805 806 try {
807 808 this.convertors_ = this.convertors_.remove(this.convertors.countUntil!(c => c == convertor));
809 } catch (Exceptione) {
810 assert(false, text("countUntil threw an exception: ", e));
811 }
812 813 returnthis;
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 TypeInfofrom() @safenothrowpureconst {
829 returntypeid(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 TypeInfoto() @safenothrowpureconst {
844 returntypeid(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 boolconvertsFrom(TypeInfofrom) const {
857 returnthis.convertors.canFind!(c => c.convertsFrom(from));
858 }
859 860 /**
861 ditto
862 **/863 boolconvertsFrom(inObjectfrom) const {
864 returnthis.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 boolconvertsTo(TypeInfoto) const {
877 returnthis.convertors.canFind!(c => c.convertsTo(to));
878 }
879 880 /**
881 ditto
882 **/883 boolconvertsTo(inObjectto) const {
884 returnthis.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 Objectconvert(inObjectfrom, TypeInfoto, RCIAllocatorallocator = theAllocator)
903 {
904 autoconvertors = this.convertors.find!(c => c.convertsFrom(from) && c.convertsTo(to));
905 906 if (!convertors.empty) {
907 returnconvertors[0].convert(from, to, allocator);
908 }
909 910 thrownewConvertorException(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 voiddestruct(refObjectconverted, RCIAllocatorallocator = theAllocator) {
923 autoconvertors = this.convertors.find!(c => c.convertsTo(converted));
924 925 if (convertors.empty) {
926 thrownewConvertorException(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 classNoOpConvertor : Convertor {
938 importstd.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 TypeInfofrom() @safenothrowpureconst {
954 returntypeid(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 TypeInfoto() @safenothrowpureconst {
969 returntypeid(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 boolconvertsFrom(TypeInfofrom) const {
982 returnthis.fromisfrom;
983 }
984 985 /**
986 ditto
987 **/988 boolconvertsFrom(inObjectfrom) const {
989 returnthis.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 boolconvertsTo(TypeInfoto) const {
1002 returnthis.toisfrom;
1003 }
1004 1005 /**
1006 ditto
1007 **/1008 boolconvertsTo(inObjectto) const {
1009 returnthis.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 Objectconvert(inObjectfrom, TypeInfoto, RCIAllocatorallocator = theAllocator)
1029 {
1030 enforce!ConvertorException(
1031 from.identifyisto,
1032 text("Cannot do no op when expected ", to, " type is not of same type as original from ", from.identify, " type")
1033 );
1034 1035 returncast() 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 voiddestruct(refObjectconverted, RCIAllocatorallocator = 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 Toconvert(To, From)(Convertorconvertor, Fromfrom, RCIAllocatorallocator = theAllocator) {
1063 importstd.typecons : scoped;
1064 staticif (is(From : Object)) {
1065 1066 Objectconverted = convertor.convert(from, typeid(To), allocator);
1067 } else {
1068 1069 Objectconverted = convertor.convert(scoped!(PlaceholderImpl!From)(from), typeid(To), allocator);
1070 }
1071 1072 staticif (is(To : Object)) {
1073 1074 returncast(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 templateAdvisedConvertor(aliasconvertor, aliasdestructor) {
1093 importstd.traits;
1094 1095 templateCheckMessage(To, From) {
1096 aliasConvertorInfo = maybeConvertor!(convertor, To, From);
1097 aliasDestructorInfo = maybeDestructor!(destructor, To);
1098 1099 importstd.traits : fullyQualifiedName;
1100 enumCheckMessage = 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.Yes1113 );
1114 1115 /// Enable it for what reason creation of convertor fails, if it is not due to type constraints.1116 debug (AdvisedConvertorDebug) {
1117 aliasDebug = ConvertorImpl!(ConvertorInfo, DestructorInfo);
1118 }
1119 }
1120 1121 templateConvertorImpl(aliasConvertorInfo, aliasDestructorInfo) {
1122 1123 aliasConvertorImpl = () {
1124 autoconvertor = newCallbackConvertor!(ConvertorInfo.Convertor, DestructorInfo.Destructor)();
1125 1126 staticif (is(From : TaggedAlgebraic!Union, Union)) {
1127 returnnewTaggedConvertor!From(convertor);
1128 } else {
1129 returnconvertor;
1130 }
1131 };
1132 }
1133 1134 templateAdvisedConvertorImplementation(To, From) {
1135 aliasConvertorInfo = maybeConvertor!(convertor, To, From);
1136 aliasDestructorInfo = maybeDestructor!(destructor, To);
1137 1138 staticif (ConvertorInfo.Yes && DestructorInfo.Yes) {
1139 1140 aliasAdvisedConvertorImplementation = ConvertorImpl!(ConvertorInfo, DestructorInfo);
1141 } else {
1142 1143 staticassert(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 templateAdvisedConvertor(aliasAccessor, aliasSetter, aliasToInspector, aliasFromInspector) {
1160 1161 enumAccessorCheck(To, From) = (is(typeof(Accessor!From()) : PropertyAccessor!(From, Object)));
1162 enumSetterCheck(To, From) = (is (typeof(Setter!To()) : PropertySetter!(To, Object)));
1163 enumFromInspectorCheck(To, From) = (is (typeof(FromInspector!From()) : Inspector!From));
1164 enumToInspectorCheck(To, From) = (is (typeof(ToInspector!To()) : Inspector!To));
1165 1166 templateCheckMessage(To, From) {
1167 importstd.traits : fullyQualifiedName;
1168 1169 enumCheckMessage = 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 aliasDebug = AdvisedConvertorImplementation!(To, From);
1195 }
1196 }
1197 1198 templateAdvisedConvertorImplementation(To, From) {
1199 1200 staticif (
1201 AccessorCheck!(To, From) &&
1202 SetterCheck!(To, From) &&
1203 FromInspectorCheck!(To, From) &&
1204 ToInspectorCheck!(To, From)
1205 ) {
1206 aliasAdvisedConvertorImplementation = (boolconversion = true, boolforce = true, boolskip = true) {
1207 importaermicioi.aedi_property_reader.core.mapper : CompositeMapper, CompositeConvertor;
1208 1209 autoconvertor = newCompositeConvertor!(To, From)();
1210 CompositeMapper!(To, From) mapper = newCompositeMapper!(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 staticif (is(From : TaggedAlgebraic!Union, Union)) {
1221 returnnewclass(convertor) TaggedConvertor!From, CombinedConvertor {
1222 privateCompositeConvertor!(To, From) cc;
1223 this(CompositeConvertor!(To, From) c) {
1224 super(c);
1225 this.cc = c;
1226 }
1227 typeof(this) convertors(Convertor[] convertors) @safenothrow { this.cc.convertors = convertors; returnthis; }
1228 typeof(this) add(Convertorconvertor) @safenothrow { this.cc.add(convertor); returnthis; }
1229 typeof(this) remove(Convertorconvertor) @safenothrow { this.cc.remove(convertor); returnthis; }
1230 };
1231 } else {
1232 returnconvertor;
1233 }
1234 };
1235 } else {
1236 1237 staticassert(false, CheckMessage!(To, From));
1238 }
1239 }
1240 }
1241 1242 templateChainedAdvisedConvertor(AdvisedConvertors...) {
1243 1244 templateCheckMessage(To, From) {
1245 importstd.traits : fullyQualifiedName;
1246 importaermicioi.util.traits : getMember;
1247 enumCheckMessage = 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 AdvisedConvertors1260 )
1261 )
1262 )
1263 );
1264 }
1265 1266 templateAdvisedConvertorImplementation(To, From) {
1267 1268 staticforeach (AdvisedConvertor; AdvisedConvertors) {
1269 importstd.traits;
1270 staticif (!is(typeof(Yes)) && is(typeof(AdvisedConvertor.AdvisedConvertorImplementation!(To, From)))) {
1271 enumYes = true;
1272 aliasAdvisedConvertorImplementation = AdvisedConvertor.AdvisedConvertorImplementation!(To, From);
1273 }
1274 }
1275 1276 staticif (!is(typeof(Yes))) {
1277 staticassert(false, CheckMessage!(To, From));
1278 }
1279 }
1280 }
1281 1282 public {
1283 enumCompositeAccessorFactory(T) = () => newCompositeAccessor!T;
1284 enumCompositeSetterFactory(T) = () => newCompositeSetter!T;
1285 enumCompositeInspectorFactory(T) = () => newCompositeInspector!T;
1286 }
1287 1288 aliasCompositeAdvisedConvertor = AdvisedConvertor!(
1289 CompositeAccessorFactory,
1290 CompositeSetterFactory,
1291 CompositeInspectorFactory,
1292 CompositeInspectorFactory1293 );