1 module dmocks.dynamic;
2 
3 import std.conv;
4 import std.traits;
5 import std.typecons;
6 
7 /++
8 + This is a very very simple class for storing a variable regardless of it's size and type
9 +/
10 abstract class Dynamic
11 {
12     // toHash, toString and opEquals are also part of this class
13     // but i'm not sure how to express that in code so this comment has to be enough:)
14 
15     /// returns stored typeinfo
16     abstract TypeInfo type();
17     /// converts stored value to given "to" type and returns 1el array of target type vals or null when conversion failed
18     abstract void[] convertTo(TypeInfo to);
19 
20     /// returns true if variable held by dynamic is convertible to given type
21     bool canConvertTo(TypeInfo to)
22     {
23         return type==to || convertTo(to) !is null;
24     }
25 }
26 
27 /// returns stored value if type T is precisely the type of variable stored, variable stored can be implicitly to that type
28 T get(T)(Dynamic d)
29 {
30     // in addition to init property requirement disallow user defined value types which can have alias this to null-able type 
31     static if (!is(T==union) && !is(T==struct) && is(typeof(T.init is null)))
32     {
33         if (d.type == typeid(typeof(null)))
34             return null;
35     }
36     if (d.type == typeid(T))
37         return ((cast(DynamicT!T)d).data());
38     void[] convertResult = d.convertTo(typeid(T));
39     return (cast(T*)convertResult)[0];
40 }
41 
42 /// a helper function for creating Dynamic obhects
43 Dynamic dynamic(T)(auto ref T t)
44 {
45     return new DynamicT!T(t);
46 }
47 
48 class DynamicT(T) : Dynamic
49 {
50     private T _data;
51     this(T t)
52     {
53         _data = t;
54     }
55 
56     ///
57     override TypeInfo type()
58     {
59         return typeid(T);
60     }
61 
62     ///
63     override string toString()
64     {
65         return _data.to!string();
66     }
67 
68     /// two dynamics are equal when they store same type and the values pass opEquals
69     override bool opEquals(Object object)
70     {
71         auto dyn = cast(DynamicT!T)object;
72         if (dyn is null)
73             return false;
74         if (dyn.type != type)
75             return false;
76 
77         return _data == dyn._data;
78     }
79 
80     ///
81     override size_t toHash()
82     {
83         return typeid(T).getHash(&_data);
84     }
85 
86     ///
87     T data()
88     {
89         return _data;
90     }
91 
92     ///
93     override void[] convertTo(TypeInfo to)
94     {
95         foreach(target;ImplicitConversionTargets!(T))
96         {
97             if (typeid(target) == to)
98             {
99                 auto ret = new target[1];
100                 ret[0] = cast(target)_data;
101                 return ret;
102             }
103         }
104         return null;
105     }
106 }
107 
108 version (DMocksTest) {
109 
110     class A
111     {
112     }
113 
114     class B : A
115     {
116     }
117 
118 
119     unittest
120     {
121         auto d = dynamic(6);
122         assert(d.toString == "6");
123         assert(d.type.toString == "int");
124         auto e = dynamic(6);
125         assert(e == d);
126         assert(e.get!int == 6);
127     }
128 
129     unittest
130     {
131         auto d = dynamic(new B);
132         assert(d.get!A !is null);
133         assert(d.get!B !is null);
134     }
135 
136     unittest
137     {
138         auto d = dynamic(null);
139         assert(d.get!A is null);
140     }
141 
142     struct C
143     {
144     }
145 
146     struct D
147     {
148         private C _c;
149         alias _c this;
150     }
151 
152     unittest {
153         int[5] a;
154         auto d = dynamic(a);
155         assert(d.get!(int[5]) == [0,0,0,0,0]);
156     }
157 
158     /+ ImplicitConversionTargets doesn't include alias thises
159     unittest
160     {
161         auto d = dynamic(D());
162         d.get!C;
163         d.get!D;
164     }
165     +/
166 }