1 module introspection.type;
2 
3 import std.traits;
4 import introspection.manifestConstant;
5 
6 version(unittest) {
7   import fluent.asserts;
8 }
9 
10 ///
11 template ArrayValueType(T : T[]) { alias ArrayValueType = T; }
12 
13 
14 /// Stores information about types
15 struct Type {
16   /// The type name, how it was defined or used in the source code
17   string name;
18 
19   /// The type name without qualifiers
20   string unqualName;
21 
22   ///
23   string fullyQualifiedName;
24 
25   /// The name of the module where the symbol is defined;
26   string module_;
27 
28   ///
29   bool isStruct;
30 
31   ///
32   bool isClass;
33 
34   ///
35   bool isInterface;
36 
37   ///
38   bool isUnion;
39 
40   ///
41   bool isArray;
42 
43   ///
44   bool isEnum;
45 
46   ///
47   bool isAssociativeArray;
48 
49   /// The keys used by the array
50   string keyType;
51 
52   /// The array values
53   string valueType;
54 
55   /// true if it is scalar type or void
56   bool isBasicType;
57 
58   /// true if it is a type defined by the compiler
59   bool isBuiltinType;
60 
61   /// true if it has `const` qualifier
62   bool isConst;
63 
64   /// true if it has `inout` qualifier
65   bool isInout;
66 
67   /// true if it has `immutable` qualifier
68   bool isImmutable;
69 
70   /// true if it has `shared` qualifier
71   bool isShared;
72 
73   /// true if it is manifest constant. eg. `enum name = "test";`
74   bool isManifestConstant;
75 }
76 
77 /// Describe a type
78 Type describeType(T)() {
79   Type type;
80 
81   type.name = T.stringof;
82   type.unqualName = Unqual!T.stringof;
83 
84   static if(__traits(compiles, fullyQualifiedName!T)) {
85     type.fullyQualifiedName = fullyQualifiedName!T;
86   }
87 
88   static if(__traits(compiles, moduleName!T)) {
89     type.module_ = moduleName!T;
90   }
91 
92   type.isBasicType = isBasicType!T;
93   type.isBuiltinType = isBuiltinType!T;
94 
95   static if(is(T == struct)) {
96     type.isStruct = true;
97   }
98 
99   static if(is(T == class)) {
100     type.isClass = true;
101   }
102 
103   static if(is(T == union)) {
104     type.isUnion = true;
105   }
106 
107   static if(is(T == interface)) {
108     type.isInterface = true;
109   }
110 
111   static if(is(T == enum)) {
112     type.isEnum = true;
113     type.isBasicType = false;
114     type.isBuiltinType = false;
115   }
116 
117   static if(isArray!T) {
118     type.isArray = true;
119     type.keyType = "size_t";
120     type.valueType = ArrayValueType!T.stringof;
121   }
122 
123   static if(isAssociativeArray!T) {
124     type.isAssociativeArray = true;
125     type.keyType = KeyType!T.stringof;
126     type.valueType = ValueType!T.stringof;
127   }
128 
129   static if(is(T == const)) {
130     type.isConst = true;
131   }
132 
133   static if(is(T == inout)) {
134     type.isInout = true;
135   }
136 
137   static if(is(T == immutable)) {
138     type.isImmutable = true;
139   }
140 
141   static if(is(T == shared)) {
142     type.isShared = true;
143   }
144 
145   static if(is(typeof(T)) && !is(typeof(&T))) {
146     type.isManifestConstant = true;
147   }
148 
149   return type;
150 }
151 
152 /// ditto
153 Type describeType(alias T)() if(!is(T == enum)) {
154   auto type = describeType!(typeof(T));
155 
156   static if(isManifestConstant!T) {
157     type.isManifestConstant = true;
158   }
159 
160   return type;
161 }
162 
163 
164 /// It should describe an int
165 unittest {
166   auto result = describeType!int;
167 
168   result.name.should.equal("int");
169   result.unqualName.should.equal("int");
170   result.fullyQualifiedName.should.equal("int");
171 
172   result.isBasicType.should.equal(true);
173   result.isBuiltinType.should.equal(true);
174   result.isConst.should.equal(false);
175   result.isInout.should.equal(false);
176   result.isImmutable.should.equal(false);
177   result.isShared.should.equal(false);
178 }
179 
180 /// It should describe an int at compile time
181 unittest {
182   enum result = describeType!int;
183 
184   result.name.should.equal("int");
185   result.unqualName.should.equal("int");
186 }
187 
188 /// It should describe a const int
189 unittest {
190   auto result = describeType!(const(int));
191 
192   result.name.should.equal("const(int)");
193   result.unqualName.should.equal("int");
194 
195   result.isBasicType.should.equal(true);
196   result.isBuiltinType.should.equal(true);
197   result.isConst.should.equal(true);
198   result.isInout.should.equal(false);
199   result.isImmutable.should.equal(false);
200   result.isShared.should.equal(false);
201 }
202 
203 /// It should describe an inout int
204 unittest {
205   auto result = describeType!(inout(int));
206 
207   result.name.should.equal("inout(int)");
208   result.unqualName.should.equal("int");
209 
210   result.isBasicType.should.equal(true);
211   result.isBuiltinType.should.equal(true);
212   result.isConst.should.equal(false);
213   result.isInout.should.equal(true);
214   result.isImmutable.should.equal(false);
215   result.isShared.should.equal(false);
216 }
217 
218 /// It should describe an immutable int
219 unittest {
220   auto result = describeType!(immutable(int));
221 
222   result.name.should.equal("immutable(int)");
223   result.unqualName.should.equal("int");
224 
225   result.isBasicType.should.equal(true);
226   result.isBuiltinType.should.equal(true);
227   result.isConst.should.equal(false);
228   result.isInout.should.equal(false);
229   result.isImmutable.should.equal(true);
230   result.isShared.should.equal(false);
231 }
232 
233 /// It should describe a shared int
234 unittest {
235   auto result = describeType!(shared(int));
236 
237   result.name.should.equal("shared(int)");
238   result.unqualName.should.equal("int");
239 
240   result.isBasicType.should.equal(true);
241   result.isBuiltinType.should.equal(true);
242   result.isConst.should.equal(false);
243   result.isInout.should.equal(false);
244   result.isImmutable.should.equal(false);
245   result.isShared.should.equal(true);
246 }
247 
248 /// It should describe a int pointer
249 unittest {
250   auto result = describeType!(int*);
251 
252   result.name.should.equal("int*");
253   result.unqualName.should.equal("int*");
254 
255   result.isBasicType.should.equal(false);
256   result.isBuiltinType.should.equal(false);
257   result.isConst.should.equal(false);
258   result.isInout.should.equal(false);
259   result.isImmutable.should.equal(false);
260   result.isShared.should.equal(false);
261 }
262 
263 /// It should describe a manifest constant
264 unittest {
265   enum a = "value";
266   auto result = describeType!(a);
267 
268   result.name.should.equal("string");
269   result.unqualName.should.equal("string");
270 
271   result.isBasicType.should.equal(false);
272   result.isBuiltinType.should.equal(true);
273   result.isConst.should.equal(false);
274   result.isInout.should.equal(false);
275   result.isImmutable.should.equal(false);
276   result.isShared.should.equal(false);
277   result.isManifestConstant.should.equal(true);
278 }
279 
280 /// It shuld describe an array of ints
281 unittest {
282   auto result = describeType!(int[]);
283 
284   result.name.should.equal("int[]");
285   result.unqualName.should.equal("int[]");
286 
287   result.isArray.should.equal(true);
288   result.keyType.should.equal("size_t");
289   result.valueType.should.equal("int");
290 }
291 
292 /// It shuld describe an array of strings
293 unittest {
294   auto result = describeType!(string[]);
295 
296   result.name.should.equal("string[]");
297   result.unqualName.should.equal("string[]");
298 
299   result.isArray.should.equal(true);
300   result.keyType.should.equal("size_t");
301   result.valueType.should.equal("string");
302 }
303 
304 /// It shuld describe an assoc array of ints
305 unittest {
306   auto result = describeType!(int[string]);
307 
308   result.name.should.equal("int[string]");
309   result.unqualName.should.equal("int[string]");
310 
311   result.isArray.should.equal(false);
312   result.isAssociativeArray.should.equal(true);
313   result.keyType.should.equal("string");
314   result.valueType.should.equal("int");
315 }
316 
317 /// It shuld describe an assoc array of strings
318 unittest {
319   auto result = describeType!(string[string]);
320 
321   result.name.should.equal("string[string]");
322   result.unqualName.should.equal("string[string]");
323 
324   result.isArray.should.equal(false);
325   result.isAssociativeArray.should.equal(true);
326   result.keyType.should.equal("string");
327   result.valueType.should.equal("string");
328 }
329 
330 /// It shuld describe a nested array
331 unittest {
332   auto result = describeType!(int[ulong][][ulong]);
333 
334   result.name.should.equal("int[ulong][][ulong]");
335   result.unqualName.should.equal("int[ulong][][ulong]");
336 
337   result.isArray.should.equal(false);
338   result.isAssociativeArray.should.equal(true);
339   result.keyType.should.equal("ulong");
340   result.valueType.should.equal("int[ulong][]");
341 }
342 
343 /// It should describe a struct
344 unittest {
345   struct Test {}
346 
347   auto result = describeType!Test;
348 
349   result.name.should.equal("Test");
350   result.unqualName.should.equal("Test");
351   result.fullyQualifiedName.should.equal("introspection.type.__unittest_L344_C1.Test");
352 
353   result.isStruct.should.equal(true);
354   result.isBasicType.should.equal(false);
355   result.isBuiltinType.should.equal(false);
356   result.isConst.should.equal(false);
357   result.isInout.should.equal(false);
358   result.isImmutable.should.equal(false);
359   result.isShared.should.equal(false);
360 }
361 
362 /// It should describe a class
363 unittest {
364   class Test {}
365 
366   auto result = describeType!Test;
367 
368   result.name.should.equal("Test");
369   result.unqualName.should.equal("Test");
370 
371   result.isStruct.should.equal(false);
372   result.isClass.should.equal(true);
373   result.isBasicType.should.equal(false);
374   result.isBuiltinType.should.equal(false);
375   result.isConst.should.equal(false);
376   result.isInout.should.equal(false);
377   result.isImmutable.should.equal(false);
378   result.isShared.should.equal(false);
379 }
380 
381 /// It should describe an union
382 unittest {
383   union Test {}
384 
385   auto result = describeType!Test;
386 
387   result.name.should.equal("Test");
388   result.unqualName.should.equal("Test");
389 
390   result.isStruct.should.equal(false);
391   result.isClass.should.equal(false);
392   result.isUnion.should.equal(true);
393   result.isBasicType.should.equal(false);
394   result.isBuiltinType.should.equal(false);
395   result.isConst.should.equal(false);
396   result.isInout.should.equal(false);
397   result.isImmutable.should.equal(false);
398   result.isShared.should.equal(false);
399 }
400 
401 /// It should describe an interface
402 unittest {
403   interface Test {}
404 
405   auto result = describeType!Test;
406 
407   result.name.should.equal("Test");
408   result.unqualName.should.equal("Test");
409 
410   result.isStruct.should.equal(false);
411   result.isClass.should.equal(false);
412   result.isUnion.should.equal(false);
413   result.isInterface.should.equal(true);
414   result.isBasicType.should.equal(false);
415   result.isBuiltinType.should.equal(false);
416   result.isConst.should.equal(false);
417   result.isInout.should.equal(false);
418   result.isImmutable.should.equal(false);
419   result.isShared.should.equal(false);
420 }
421 
422 /// It should describe an enum
423 unittest {
424   enum Test {
425     a,b,c
426   }
427 
428   auto result = describeType!Test;
429 
430   result.name.should.equal("Test");
431   result.unqualName.should.equal("Test");
432   result.module_.should.equal("introspection.type");
433 
434   result.isStruct.should.equal(false);
435   result.isClass.should.equal(false);
436   result.isUnion.should.equal(false);
437   result.isInterface.should.equal(false);
438   result.isEnum.should.equal(true);
439   result.isBasicType.should.equal(false);
440   result.isBuiltinType.should.equal(false);
441   result.isConst.should.equal(false);
442   result.isInout.should.equal(false);
443   result.isImmutable.should.equal(false);
444   result.isShared.should.equal(false);
445 }