1 module glued.context; 2 3 import std.variant; 4 import std.meta; 5 import std.traits; 6 7 import poodinis: DependencyContainer; 8 9 import glued.stereotypes; 10 import glued.singleton; 11 import glued.mirror; 12 import glued.scan; 13 import glued.utils; 14 15 struct StereotypeDefinition(S) if (is(S == struct)) { 16 S stereotype; 17 LocatedAggregate target; 18 } 19 20 class BackboneContext { 21 private LocatedAggregate[] _tracked; 22 private Variant[][LocatedAggregate] _stereotypes; 23 24 void track(string m, string n)() { 25 version(glued_debug) { 26 pragma(msg, "Tracking ", m, "::", n); 27 } 28 alias aggr = import_!(m, n); 29 static if (qualifiesForTracking!(aggr)()){ 30 version(glued_debug) { 31 pragma(msg, "qualifies!"); 32 } 33 auto a = aggregate!(m, n)(); 34 _tracked ~= a; 35 36 void gatherStereotypes(S)(S s){ 37 _stereotypes[aggregate!(S)()] ~= Variant(StereotypeDefinition!S(s, a)); 38 } 39 static foreach (alias s; getStereotypes!aggr) { 40 gatherStereotypes(s); 41 } 42 } 43 } 44 45 @property 46 public LocatedAggregate[] tracked(){ 47 return this._tracked; 48 } 49 50 @property 51 public Variant[][LocatedAggregate] stereotypes(){ 52 return this._stereotypes; 53 } 54 55 static bool qualifiesForTracking(alias T)(){ 56 return hasAnnotation!(T, Tracked); 57 } 58 } 59 60 //struct Processor { 61 // string canHandle="canHandle"; 62 // string handle="handle"; 63 // string before=""; 64 // string after=""; 65 //} 66 67 ////todo this seems well suited for annotations module 68 //template ResolveMemberAnnotation(alias T, A, string fromField="value") { 69 // static if (!hasOneAnnotation!(T, A)){ 70 // enum ResolveMemberAnnotation = None(); 71 // } else { 72 // //todo actually member name 73 // enum methodName = __traits(getMember, getAnnotation!(T, A), fromField); 74 // static if (methodName.length == 0){ 75 // enum ResolveMemberAnnotation = None(); 76 // } 77 // else 78 // { 79 // static if (!hasMember!(T, methodName)){ 80 // enum ResolveMemberAnnotation = None(); 81 // } else { 82 // pragma(msg, "resolved ", T, ":", A, "/", fromField, " to ", methodName); 83 // alias ResolveMemberAnnotation = __traits(getMember, T, methodName); 84 // } 85 // } 86 // 87 // } 88 //} 89 90 //version(unittest){ 91 // @Processor 92 // class P { 93 // bool canHadle(S)(){ 94 // return true; 95 // } 96 // 97 // void handle(S)(GluedInternals internals){ 98 // pragma(msg, "Handle ", S); 99 // } 100 // } 101 //} 102 103 //@Processor 104 //class SimpleDiProcessor { 105 // enum canHandle(A) = isMarkedAsStereotype!(A, Component); 106 // 107 // void handle(TypeWithStereotype)(GluedInternals internals){ 108 // import std.stdio; 109 // import std.traits; 110 // writeln("registering ", fullyQualifiedName!TypeWithStereotype); 111 // internals.diContext.register!TypeWithStereotype; 112 // } 113 //} 114 115 /** 116 * Interface of each processing step; not enforced, because it has to be templated, 117 * but is useful for documentational usage. 118 */ 119 //interface Processor { 120 // void before(); 121 // bool canHadle(A)(); 122 // void handle(A)(GluedInternals internals); 123 // void after(); 124 //} 125 126 struct SimpleDiProcessor { 127 static void before(){} 128 129 static bool canHandle(A)(){ 130 pragma(msg, "canHandle ", A, " -> ", isMarkedAsStereotype!(A, Component), " ; ", getStereotype!(A, Component)); 131 return is(A == class) && isMarkedAsStereotype!(A, Component); 132 } 133 134 static void handle(A)(GluedInternals internals){ 135 import std.stdio; 136 import std.traits; 137 writeln("registering ", fullyQualifiedName!A); 138 internals.diContext.register!A; 139 } 140 141 static void after(){} 142 } 143 144 struct GluedInternals { 145 shared DependencyContainer diContext; 146 } 147 148 synchronized class GluedContext { 149 private GluedInternals internals; 150 151 alias processors = AliasSeq!(SimpleDiProcessor); 152 153 this(){ 154 internals = GluedInternals(new shared DependencyContainer()); 155 } 156 157 private void before(){ 158 static foreach (p; processors) 159 p.before(); 160 } 161 162 private void after(){ 163 static foreach (p; processors) 164 p.after(); 165 } 166 167 void scan(alias scannables)(){ 168 enum scanConsumer(string m, string n) = "track!(\""~m~"\", \""~n~"\")();"; 169 mixin unrollLoopThrough!(scannables, "void doScan() { ", scanConsumer, "}"); 170 171 before(); 172 doScan(); 173 after(); 174 } 175 176 @property 177 shared(DependencyContainer) internalDiContext(){ 178 return internals.diContext; 179 } 180 181 void track(string m, string n)(){ 182 version(glued_debug) { 183 pragma(msg, "Tracking ", m, "::", n, " by ", typeof(this)); 184 } 185 alias aggr = import_!(m, n); 186 static if (qualifiesForTracking!(aggr)()){ 187 static foreach (p; processors){ 188 static if (p.canHandle!(aggr)()){ 189 p.handle!(aggr)(internals); 190 } 191 } 192 } 193 } 194 195 196 private static bool qualifiesForTracking(alias T)(){ 197 return hasAnnotation!(T, Tracked); 198 } 199 }