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