1 module injected.injection;
2 
3 import std.meta : staticMap, Filter;
4 
5 import injected.resolver : Resolver;
6 
7 /**
8  * Annotation for an injection.
9  * It can optionally include a name to use for the lookup.
10  */
11 struct Injected(T, string _name = null) {
12     alias Type = T;
13     enum name = _name;
14 }
15 
16 /**
17  * Annotation marking a class or factory function as
18  * Injectable. The
19  */
20 struct Injectable(T...) {
21     /**
22      * A tuple of the Injections
23      */
24     alias Injections = T;
25 }
26 
27 template getInjectables(alias symbol) {
28     private template isInjectable(alias T) {
29         enum isInjectable = is(T: Injectable!(U), U...);
30     }
31 
32     alias getInjectables = Filter!(isInjectable, __traits(getAttributes, symbol));
33 }
34 
35 template toInjected(T) {
36     static if (is(T: Injected!U, U) || is(T: Injected!(U, n), U, string n)) {
37         alias toInjected = T;
38     } else {
39         alias toInjected = Injected!T;
40     }
41 }
42 
43 /**
44  * Take an AliasSeq of types and/or `Injected` values and return
45  * a ValueSeq of `Injected` values, where any Types are converted to
46  * nameless `Injected` values for that type.
47  */
48 template extractInjected(Args...) {
49     alias extractInjected = staticMap!(toInjected, Args);
50 }
51 
52 ///
53 unittest {
54     import std.meta : AliasSeq;
55 
56     static assert(is(extractInjected!(int, string) == AliasSeq!(Injected!int, Injected!string)));
57     static assert(is(extractInjected!(Injected!float) == AliasSeq!(Injected!float)));
58     static assert(is(extractInjected!(Injected!(int, "foo")) == AliasSeq!(Injected!(int, "foo"))));
59 
60     static assert(is(extractInjected!(Injected!(string, "name"), int, Injected!(void*)) == AliasSeq!(Injected!(string, "name"), Injected!int, Injected!(void*))));
61 
62     static assert(!__traits(compiles, extractInjected!("hi", int)));
63 }
64 
65 alias InjectionType(T) = toInjected!T.Type;
66 
67 auto injectionSeq(Args...)(Resolver resolver) {
68     import std.typecons : Tuple;
69     alias InjectionTypes = staticMap!(InjectionType, Args);
70 
71     Tuple!(InjectionTypes) injections;
72     foreach (i, injected; extractInjected!Args) {
73         alias T = InjectionType!injected;
74         static if (injected.name is null) {
75             injections[i] = resolver.resolve!T();
76         } else {
77             injections[i] = resolver.resolve!T(injected.name);
78         }
79     }
80     return injections;
81 }
82 
83 unittest {
84     import std.meta : AliasSeq;
85     import std.typecons : tuple;
86     import injected.container;
87 
88     auto container = makeContainer();
89     container.value!int("a", 10);
90     container.value!int("b", 20);
91     container.value!string("name");
92     container.value!float(3.14f);
93 
94 
95     assert(injectionSeq!(Injected!string)(container) == tuple("name"));
96     assert(injectionSeq!(Injected!int)(container) == tuple(10));
97     assert(injectionSeq!(Injected!(int, "a"))(container) == tuple(10));
98     assert(injectionSeq!(Injected!(int,"b"))(container) == tuple(20));
99     assert(injectionSeq!(int, string, float)(container) == tuple(10, "name", 3.14f));
100 
101     assert(injectionSeq!(Injected!string, Injected!(int, "b"), Injected!float)(container) ==
102            tuple("name", 20, 3.14f));
103 }