1 module glued.codescan.unrollscan;
2 
3 import std.conv: to;
4 import std.traits;
5 import std.meta;
6 import std..string;
7 
8 import glued.mirror;
9 import glued.utils;
10 
11 public import glued.codescan.scannable;
12 
13 //DEV NOTE ON VISIBILITY LEVELS
14 //
15 //following functions are called statically during the mixin; because of that
16 //they need to be public, to be available on mixin point;
17 //if they are private, then module mixing them in won't be able to resolve
18 //them, even though they are in the same module as mixin (since mixins are
19 //evaluated at mixin point, not declaration point)
20 
21 string scanModule(string name, alias aggregateConsumer)(){
22     StringBuilder builder;
23     mixin("static import "~name~";");
24     mixin("alias mod_ = "~name~";");
25     static foreach (alias mem; __traits(allMembers, mod_)){
26         static if (__traits(compiles, __traits(getMember, mod_, mem))){
27             static if (is(__traits(getMember, mod_, mem))){
28                 // if we encounter enum of form "enum Name;" (or "enum Name: int;")
29                 // we get:
30                 //    Error: enum ...Name is forward referenced looking for base type
31                 // or:
32                 //    Error: enum ...Name is forward referenced when looking for stringof
33                 // etc
34                 // To avoid that we filter out enums without members, which loosely relates to aforementioned situations
35                 //todo investigate how loosely
36                 static if (  
37                     !is(__traits(getMember, mod_, mem) == enum) || 
38                     EnumMembers!(__traits(getMember, mod_, mem)).length > 0
39                 )
40                 builder.append(aggregateConsumer!(name, mem));
41             }
42         }
43     }
44     return builder.result;
45 }
46 
47 enum NoOp(T...) = "";
48 
49 string scanIndexModule(string index, alias scannable, alias aggregateConsumer, alias bundleConsumer)(){
50     StringBuilder builder;
51     mixin("static import "~index~";");
52     mixin("alias mod_ = "~index~";");
53     static if (mod_.Index.hasBundle){
54         builder.append(
55             bundleConsumer!(mod_.Index.bundleModule)
56         );
57     }
58     static if (mod_.Index.importablePackage)
59         builder.append(
60             scanModule!(mod_.Index.packageName, aggregateConsumer)()
61         );
62     static foreach (string submodule; EnumMembers!(mod_.Index.submodules)){
63         builder.append(
64             scanModule!(submodule, aggregateConsumer)()
65         );
66     }
67     static foreach (string subpackage; EnumMembers!(mod_.Index.subpackages)){
68         builder.append(
69             prepareScan!(
70                 scannable.withRoot(subpackage), 
71                 "", 
72                 aggregateConsumer, 
73                 bundleConsumer, 
74                 ""
75             )()); //todo qualifiers
76     }
77     return builder.result;
78 }
79 
80 string prepareScan(alias scannable, string setup, alias aggregateConsumer, alias bundleConsumer, string teardown)(){
81     StringBuilder builder;
82     builder.append(setup);
83     
84     builder.append(
85         scanIndexModule!(
86             scannable.root~"."~scannable.prefix~"_index", 
87             scannable, 
88             aggregateConsumer, 
89             bundleConsumer
90         )()
91     );
92     
93     version(unittest){
94         builder.append(
95             scanIndexModule!(
96                 scannable.root~"."~scannable.testPrefix~"_index", 
97                 scannable, 
98                 aggregateConsumer, 
99                 bundleConsumer
100             )()
101         );
102     }
103     
104     builder.append(teardown);
105     builder.removeEmptyLines();
106     return builder.result;
107 }
108 
109 mixin template unrollLoopThrough(alias scannable, string setup, alias aggregateConsumer, alias bundleConsumer, string teardown, 
110     string f=__FILE__, int l=__LINE__, string m=__MODULE__, string foo=__FUNCTION__, string prettyFoo=__PRETTY_FUNCTION__)
111     if (isScannable!(scannable))
112 {
113     import glued.logging;
114     mixin CreateLogger!();
115     mixin(
116         Logger.logged!(f, l, m, foo, prettyFoo)().value!(
117             prepareScan!(
118                 scannable, 
119                 setup, 
120                 aggregateConsumer, 
121                 bundleConsumer, 
122                 teardown
123             )()
124         )
125     );
126 }