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 );