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 	aermicioi
29 **/
30 module aermicioi.aedi.storage.wrapper;
31 
32 import std.traits;
33 import std.meta : staticMap;
34 
35 @safe:
36 /**
37 Interface for components that wrap a component of type T. Provides boxing and automatic unboxing of wrapped values.
38 **/
39 interface Wrapper(T) {
40     public {
41 
42         /**
43         Get wrapped value out of wrapper.
44 
45         Returns:
46             T the wrapped value
47         **/
48         @property ref inout(T) value() inout return scope;
49 
50         /**
51         Alias wrapper to T for automatic unboxing of values.
52         **/
53         alias value this;
54     }
55 }
56 
57 /**
58 Interface for components that denotes tha ability to be casted to a particular type T.
59 **/
60 interface Castable(T) {
61     public {
62 
63         /**
64         Get a representation of current component in type T.
65 
66         Returns:
67             T component transformed into T component.
68         **/
69         @property inout(T) casted() inout return scope;
70 
71         /**
72         Alias casted type for automatic casting of component.
73         **/
74         alias casted this;
75     }
76 }
77 
78 /**
79 Wrapper over some component of type T.
80 
81 Wraps up a value of type T, and aliases itself to it. This object is used
82 wrap any component that is not of reference type (class or interface),
83 in order to be saveable into an object storage.
84 Also thanks to alias value this semantics, in D is possible to do automatic
85 unboxing of values, just like Java does with simple values :P.
86 **/
87 class WrapperImpl(T) : Wrapper!T {
88     mixin WrapperMixin!T;
89 }
90 
91 /**
92 ditto
93 **/
94 class CastableWrapperImpl(T, Castables...) : Wrapper!T, staticMap!(toCastable, Castables) {
95     mixin WrapperMixin!T;
96 
97     mixin CastableMixin!(Castables);
98 }
99 
100 /**
101 Downcast an object to component of T type.
102 
103 This family of functions are aware of Wrapper!T and Castable!T interfaces, and
104 therefore will use them in downcasting process to T component. In case if T is
105 of class or interface type, the object is attempted to be downcast to T type first
106 if it fails, it will continue to cast to Wrapper!T and then Castable!T to find whether
107 component is downcastable to T type. If so, component of T type is returned, otherwise
108 an exception is thrown. For non referenced types, direct downcast is ignored and only
109 casting to Wrapper!T and Castable!T is attempted. In case if passed component is
110 an object implementing Wrapper!T or Castable!T, contents of those wrappers is returned.
111 For a component that does not implement those types, component itself will be returned.
112 
113 Params:
114 	T = the expected type of resolved component.
115 	component = component to be downcasted.
116 
117 Throws:
118 	InvalidCastException when resolved component is not of expected type.
119 
120 Returns:
121 	T referenced object
122 **/
123 @trusted T unwrap(T)(inout(Object) component)
124     if (is(T == class) || is(T == interface)) {
125     import aermicioi.aedi.exception.invalid_cast_exception : InvalidCastException;
126 
127     {
128         T result = cast(T) component;
129 
130         if (result !is null) {
131             return result;
132         }
133     }
134 
135     {
136         Wrapper!T result = cast(Wrapper!T) component;
137 
138         if (result !is null) {
139             return result.value;
140         }
141     }
142 
143     {
144         Castable!T result = cast(Castable!T) component;
145 
146         if (result !is null) {
147 
148             return result.casted;
149         }
150     }
151 
152     throw new InvalidCastException("Could not downcast component of ${actual} type, expected ${expected} type", null, typeid(T), component.classinfo);
153 }
154 
155 /**
156 ditto
157 **/
158 @trusted T unwrap(T)(inout(Object) component)
159     if (!(is(T == class) || is(T == interface))) {
160     import aermicioi.aedi.exception.invalid_cast_exception : InvalidCastException;
161 
162     {
163         Wrapper!T result = cast(Wrapper!T) component;
164 
165         if (result !is null) {
166             return result.value;
167         }
168     }
169 
170     {
171         Castable!T result = cast(Castable!T) component;
172 
173         if (result !is null) {
174 
175             return result.casted;
176         }
177     }
178 
179     throw new InvalidCastException("Could not downcast component of ${actual} type, expected ${expected} type", null, typeid(T), component.classinfo);
180 }
181 
182 /**
183 ditto
184 **/
185 @safe Z unwrap(T : Castable!Z, Z)(T castable) {
186     return castable.casted;
187 }
188 
189 /**
190 ditto
191 **/
192 @safe Z unwrap(T : Wrapper!Z, Z)(T wrapper) {
193 
194     return wrapper.value;
195 }
196 
197 /**
198 ditto
199 **/
200 @safe T unwrap(T)(auto ref T wrapped) {
201     return wrapped;
202 }
203 
204 private {
205 
206     mixin template WrapperMixin(T) {
207         private {
208             T value_;
209         }
210 
211         public {
212 
213             this() @disable;
214 
215             this(ref T value) {
216                 this.value_ = value;
217             }
218 
219             this(T value) {
220                 this.value_ = value;
221             }
222 
223             @property ref inout(T) value() inout return scope {
224                 return this.value_;
225             }
226         }
227     }
228 
229     mixin template CastableMixin() {
230 
231     }
232 
233     mixin template CastableMixin(Type) {
234         @property {
235             inout(Type) casted() inout return scope {
236                 return cast(inout(Type)) this.value;
237             }
238         }
239     }
240 
241     mixin template CastableMixin(Type, Second, More...) {
242 
243         mixin CastableMixin!(Type);
244 
245         mixin CastableMixin!(Second);
246 
247         mixin CastableMixin!(More);
248     }
249 
250     alias toCastable(T) = Castable!T;
251 }