1 module autowrap.reflection;
2 
3 
4 public import autowrap.types: isModule, Modules, Module, Ignore;
5 import std.meta: allSatisfy;
6 import std.typecons: Flag, No;
7 
8 
9 private enum isString(alias T) = is(typeof(T) == string);
10 
11 
12 template AllConstants(Modules modules) {
13     import std.algorithm: map;
14     import std.array: join;
15     import std.typecons: Yes, No;  // needed for Module.toString in the mixin
16 
17     enum modulesList = modules.value.map!(a => a.toString).join(", ");
18     mixin(`alias AllConstants = AllConstants!(`, modulesList, `);`);
19 }
20 
21 template AllConstants(Modules...) if(allSatisfy!(isModule, Modules)) {
22     import std.meta: staticMap;
23     alias AllConstants = staticMap!(Constants, Modules);
24 }
25 
26 template Constants(Module module_) {
27     import mirror.meta: MirrorModule = Module;
28     import std.meta: Filter;
29 
30     alias mod = MirrorModule!(module_.name);
31 
32     private enum isConstant(alias var) = var.isConstant;
33 
34     alias Constants = Filter!(isConstant, mod.Variables);
35 }
36 
37 
38 template AllFunctions(Modules modules) {
39     import std.algorithm: map;
40     import std.array: join;
41     import std.typecons: Yes, No;  // needed for Module.toString in the mixin
42 
43     enum modulesList = modules.value.map!(a => a.toString).join(", ");
44     mixin(`alias AllFunctions = AllFunctions!(`, modulesList, `);`);
45 }
46 
47 template AllFunctions(Modules...) if(allSatisfy!(isString, Modules)) {
48     import std.meta: staticMap;
49     enum module_(string name) = Module(name);
50     alias AllFunctions = staticMap!(Functions, staticMap!(module_, Modules));
51 }
52 
53 template AllFunctions(Modules...) if(allSatisfy!(isModule, Modules)) {
54     import std.meta: staticMap;
55     alias AllFunctions = staticMap!(Functions, Modules);
56 }
57 
58 template Functions(Module module_) {
59     mixin(`import dmodule = ` ~ module_.name ~ `;`);
60     alias Functions = Functions!(dmodule, module_.alwaysExport, module_.ignoredSymbols);
61 }
62 
63 template Functions(alias module_, Flag!"alwaysExport" alwaysExport = No.alwaysExport, Ignore[] ignoredSymbols = [])
64     if(!is(typeof(module_) == string))
65 {
66     import mirror.meta: MirrorModule = Module, FunctionSymbol;
67     import std.meta: staticMap, Filter, templateNot;
68     import std.traits: moduleName;
69     import std.algorithm: canFind;
70 
71     alias mod = MirrorModule!(moduleName!module_);
72     enum isExport(alias F) = isExportFunction!(F.symbol, alwaysExport);
73     enum shouldIgnore(alias F) = ignoredSymbols.canFind!(a => a.identifier == F.identifier);
74 
75     alias Functions = Filter!(templateNot!shouldIgnore,
76                               Filter!(isExport, mod.FunctionsBySymbol));
77 }
78 
79 
80 template AllAggregates(Modules modules) {
81     import std.algorithm: map;
82     import std.array: join;
83     import std.typecons: Yes, No;  // needed for Module.toString in the mixin
84 
85     enum modulesList = modules.value.map!(a => a.toString).join(", ");
86     mixin(`alias AllAggregates = AllAggregates!(`, modulesList, `);`);
87 }
88 
89 template AllAggregates(ModuleNames...) if(allSatisfy!(isString, ModuleNames)) {
90     import std.meta: staticMap;
91 
92     enum module_(string name) = Module(name);
93     enum Modules = staticMap!(module_, ModuleNames);
94 
95     alias AllAggregates = AllAggregates!(staticMap!(module_, ModuleNames));
96 }
97 
98 template AllAggregates(Modules...) if(allSatisfy!(isModule, Modules)) {
99     import std.meta: Filter, NoDuplicates, staticMap;
100     import std.traits: isCopyable;
101 
102     alias AllAggregates = Filter!(isCopyable, NoDuplicates!(staticMap!(AllAggregatesInModule, Modules)));
103 }
104 
105 private template AllAggregatesInModule(Module module_) {
106     import mirror.meta: MirrorModule = Module;
107     import std.meta: NoDuplicates, Filter, staticMap, templateNot;
108     import std.algorithm: canFind;
109 
110     alias mod = MirrorModule!(module_.name);
111     enum shouldIgnore(T) = module_.ignoredSymbols.canFind!(a => a.identifier == T.stringof);
112 
113     alias AllAggregatesInModule =
114         Filter!(templateNot!shouldIgnore,
115                 NoDuplicates!(
116                     Filter!(isUserAggregate,
117                             staticMap!(PrimordialType, mod.AllAggregates))));
118 }
119 
120 
121 // if a type is a struct or a class
122 template isUserAggregate(A...) if(A.length == 1) {
123     import std.datetime;
124     import std.traits: Unqual, isInstanceOf;
125     import std.typecons: Tuple;
126     import core.time: Duration;
127 
128     alias T = A[0];
129 
130     enum isUserAggregate =
131         !is(Unqual!T == DateTime) &&
132         !is(Unqual!T == Date) &&
133         !is(Unqual!T == TimeOfDay) &&
134         !is(Unqual!T == Duration) &&
135         !isInstanceOf!(Tuple, T) &&
136         (is(T == struct) || is(T == class) || is(T == enum));
137 }
138 
139 
140 // T -> T, T[] -> T, T[][] -> T, T* -> T
141 template PrimordialType(T) {
142     import mirror.traits: FundamentalType;
143     import std.traits: Unqual;
144     alias PrimordialType = Unqual!(FundamentalType!T);
145 }
146 
147 
148 package template isExportFunction(alias F, Flag!"alwaysExport" alwaysExport = No.alwaysExport) {
149     import std.traits: isFunction;
150 
151     static if(!isFunction!F)
152         enum isExportFunction = false;
153     else {
154         version(AutowrapAlwaysExport) {
155             enum linkage = __traits(getLinkage, F);
156             enum isExportFunction = linkage != "C" && linkage != "C++";
157         } else version(AutowrapAlwaysExportC) {
158             enum linkage = __traits(getLinkage, F);
159             enum isExportFunction = linkage == "C" || linkage == "C++";
160         } else
161             enum isExportFunction = isExportSymbol!(F, alwaysExport);
162     }
163 }
164 
165 
166 private template isExportSymbol(alias S, Flag!"alwaysExport" alwaysExport = No.alwaysExport) {
167     static if(__traits(compiles, __traits(getProtection, S)))
168         enum isExportSymbol = isPublicSymbol!S && (alwaysExport || __traits(getProtection, S) == "export");
169     else
170         enum isExportSymbol = false;
171 }
172 
173 
174 private template isPublicSymbol(alias S) {
175     enum isPublicSymbol = __traits(getProtection, S) == "export" || __traits(getProtection, S) == "public";
176 }