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 }