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 }