1 module glued.application.scanlisteners.concrete;
2 
3 import std.traits;
4 
5 import glued.logging;
6 
7 import glued.codescan.scannable;
8 import glued.codescan.listener;
9 
10 import glued.application.stereotypes;
11 import glued.application.di.providers;
12 import glued.application.di.annotations;
13 
14 import dejector;
15 
16 class ConcreteTypesListener: ScanListener!Dejector {
17     mixin CreateLogger;
18     private Logger log;
19     private Dejector injector;
20 
21     void init(Dejector injector)
22     {
23         this.injector = injector;
24         log = Logger(injector.get!LogSink);
25     }
26 
27     void onScannable(alias scannable)() if (isScannable!scannable)
28     {
29         //todo track what scannable does type and its binding come from (?) - this is tricky and far in the future
30     }
31     
32     void onType(A)() //A as in "aggregate"
33     {
34         static if (canHandle!A()) { //todo log about it
35             static if (isMarkedAsStereotype!(A, Component)) {
36                 handleComponent!A();
37             }
38             static if (isMarkedAsStereotype!(A, Configuration)){
39                 handleConfiguration!A();
40             }
41         }
42     }
43     
44     void onBundleModule(string modName)()
45     {
46     }
47     
48     void onScannerFreeze()
49     {
50     }
51 
52     private static bool canHandle(A)(){
53         return is(A == class);
54     }
55     
56     private void handleComponent(A)(){
57         log.info.emit("Binding ", fullyQualifiedName!A);
58         injector.bind!(A)(new ComponentClassProvider!A(log.logSink));
59         log.info.emit("Bound ", fullyQualifiedName!A, " based on its class definition");
60     }
61 
62     private void handleConfiguration(A)(){
63         log.info.emit("Binding based on configuration ", fullyQualifiedName!A);
64         injector.bind!(A, Singleton)(new ComponentClassProvider!A(log.logSink));
65         static foreach (name; __traits(allMembers, A)){
66             static if (__traits(getProtection, __traits(getMember, A, name)) == "public" &&
67             isFunction!(__traits(getMember, A, name))) {
68                 static foreach (i, overload; __traits(getOverloads, A, name)) {
69                     static if (hasAnnotation!(overload, Component)) {
70                         static if (!hasAnnotation!(overload, IgnoreResultBinding)){
71                             //todo reuse the same provider for many bindings
72                             log.info.emit("Binding type "~fullyQualifiedName!(ReturnType!overload)~" with method ", fullyQualifiedName!A, ".", name, "[#", i, "]");
73                             injector.bind!(ReturnType!overload)(new ConfigurationMethodProvider!(A, name, i)(log.logSink));
74                             log.info.emit("Bound "~fullyQualifiedName!(ReturnType!overload)~" with method ", fullyQualifiedName!A, ".", name, "[#", i, "]");
75                         } //todo else assert exactly one ignore and any Bind
76 
77                         static foreach (bind; getAnnotations!(overload, Bind)){
78                             log.info.emit("Binding type "~fullyQualifiedName!(Bind.As)~" with method ", fullyQualifiedName!A, ".", name, "[#", i, "]");
79                             injector.bind!(Bind.As)(new ConfigurationMethodProvider!(A, name, i)(log.logSink));
80                             log.info.emit("Bound "~fullyQualifiedName!(Bind.As)~" with method ", fullyQualifiedName!A, ".", name, "[#", i, "]");
81                         }
82                     }
83                 }
84             }
85         }
86         log.info.emit("Bound chosen members on configuration ", fullyQualifiedName!A);
87     }
88 }