1 module introspection.module_;
2 
3 import std.traits;
4 import std.conv;
5 
6 import introspection.type;
7 import introspection.callable;
8 import introspection.aggregate;
9 import introspection.template_;
10 import introspection.location;
11 import introspection.protection;
12 import introspection.property;
13 import introspection.unittest_;
14 import introspection.manifestConstant;
15 
16 version(unittest) {
17   import fluent.asserts;
18 }
19 
20 /// Stores information about modules
21 struct Module {
22   /// The constant name
23   string name;
24 
25   ///
26   string fullyQualifiedName;
27 
28   ///
29   Callable[] functions;
30 
31   ///
32   Aggregate[] aggregates;
33 
34   ///
35   Template[] templates;
36 
37   ///
38   Property[] globals;
39 
40   ///
41   ManifestConstant[] manifestConstants;
42 
43   ///
44   UnitTest[] unitTests;
45 
46   ///
47   Location location;
48 }
49 
50 /// Describe a module and containing members
51 Module describeModule(alias T, bool withUnitTests = false)() if(__traits(isModule, T)) {
52   Module module_;
53 
54   module_.name = T.stringof;
55   module_.fullyQualifiedName = fullyQualifiedName!T;
56   Location location;
57 
58   static foreach(member; __traits(allMembers, T)) static if(member.stringof != `"object"` && __traits(compiles, __traits(getMember, T, member))) {{
59     alias M = __traits(getMember, T, member);
60 
61     static if(__traits(compiles, __traits(getLocation, M))) {
62       if(module_.location.file == "") {
63         module_.location.file = __traits(getLocation, M)[0];
64       }
65     }
66 
67     static if(isCallable!M) {
68       static foreach(index, overload; __traits(getOverloads, T, member)) {
69         module_.functions ~= describeCallable!(overload, index);
70       }
71     }
72     else static if(__traits(isTemplate, M)) {
73       module_.templates ~= describeTemplate!M;
74     }
75     else static if(isTypeTuple!M && isAggregateType!M) {
76       module_.aggregates ~= describeAggregate!M;
77     }
78     else static if(isManifestConstant!M) {
79       module_.manifestConstants ~= describeManifestConstant!(T, member);
80     }
81     else static if(__traits(compiles, describeProperty!M)) {
82       module_.globals ~= describeProperty!M;
83     }
84   }}
85 
86   static if(withUnitTests) {
87     module_.unitTests = describeUnitTests!T;
88   }
89 
90   return module_;
91 }
92 
93 /// It should describe a module with all functions, aggregates, templates, and global vars
94 unittest {
95   import introspection.test.moduleDef;
96 
97   auto result = describeModule!(introspection.test.moduleDef, true);
98 
99   result.name.should.equal("module moduleDef");
100   result.fullyQualifiedName.should.equal("introspection.test.moduleDef");
101 
102   /// check functions
103   result.functions.length.should.equal(2);
104   result.functions[0].name.should.equal("testFunction");
105   result.functions[1].name.should.equal("privateFunction");
106 
107   /// check aggregates
108   result.aggregates.length.should.equal(4);
109   result.aggregates[0].name.should.equal("TestStructure");
110   result.aggregates[1].name.should.equal("TestClass");
111   result.aggregates[2].name.should.equal("TestInterface");
112   result.aggregates[3].name.should.equal("TestUnion");
113 
114   /// check templates
115   result.templates.length.should.equal(2);
116   result.templates[0].name.should.equal("TestTpl");
117   result.templates[1].name.should.equal("templatedFunction");
118 
119   /// check globals
120   result.globals.length.should.equal(1);
121   result.globals[0].name.should.equal("globalVar");
122 
123   /// check manifest constants
124   result.manifestConstants.length.should.equal(1);
125   result.manifestConstants[0].name.should.equal("someManifestConstant");
126 
127   /// check unittests
128   result.unitTests.length.should.equal(2);
129 
130   ///
131   result.location.file.should.equal("source/introspection/test/moduleDef.d");
132   result.location.line.should.equal(0);
133   result.location.column.should.equal(0);
134 }
135 
136 /// It should be able to describe the std.array module
137 unittest {
138   import std.array;
139 
140   auto result = describeModule!(std.array);
141 
142   result.name.should.equal("module array");
143   result.fullyQualifiedName.should.equal("std.array");
144 
145   result.functions.length.should.equal(1);
146   result.functions[0].name.should.equal("_d_newarrayU");
147 
148   result.templates.length.should.equal(31);
149 }
150 
151 /// It should be able to describe the std.stdio module
152 unittest {
153   auto result = describeModule!(std.stdio);
154 
155   result.name.should.equal("module stdio");
156   result.fullyQualifiedName.should.equal("std.stdio");
157 
158   result.functions.length.should.equal(22);
159   result.functions[0].name.should.equal("flockfile");
160 
161   result.templates.length.should.equal(15);
162 }