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