1 module injected.container; 2 3 import std.traits : isCallable; 4 import std.typecons : Flag, Yes, No; 5 6 import injected.resolver; 7 import injected.provider; 8 9 /** 10 * A dependency injection container. 11 * 12 * Providers are registered with a `TypeInfo` and an optional name. 13 * Values can then be resolved using the `TypeInfo` and name. 14 */ 15 interface Container : Resolver { 16 /** 17 * Register a `Provider` for it's `TypeInfo`. A name can also be provided 18 * to disambiguate when multiple providers are needed 19 * 20 * Notes: `provider.providedType` should be a subType of the type represented by `TypeInfo`. 21 */ 22 protected void addProvider(TypeInfo type, Provider provider); 23 /// ditto 24 protected void addProvider(TypeInfo type, string name, Provider provider); 25 26 /** 27 * Register a single value. 28 * 29 * A `ValueProvider` is used to provide the value. 30 */ 31 final void value(T)(string name, T value) { 32 addProvider(typeid(T), name, new ValueProvider!T(value)); 33 } 34 35 /// ditto 36 final void value(T)(T value) { 37 addProvider(typeid(T), new ValueProvider!T(value)); 38 } 39 40 /** 41 * Register a Class. 42 * 43 * Instances are provided using a `ClassProvider` that injects dependencies using 44 * this container. 45 */ 46 final void register(I, C: I = I)(Flag!"asSingleton" asSingleton = Yes.asSingleton) if (is(C == class)) { 47 addProvider(typeid(I), maybeSingleton(new ClassProvider!C(this), asSingleton)); 48 } 49 /// ditto 50 final void register(I, C: I = I)(string name, Flag!"asSingleton" asSingleton = Yes.asSingleton) if (is(C == class)) { 51 addProvider(typeid(I), name, maybeSingleton(new ClassProvider!C(this), asSingleton)); 52 } 53 54 /** 55 * Register a factory function for a type. 56 */ 57 final void factory(T, alias F)(Flag!"asSingleton" asSingleton = Yes.asSingleton) if (isCallable!F && is(ReturnType!F: T)) { 58 addProvider(typeid(T), maybeSingleton(new FactoryProvider!F(this), asSingleton)); 59 } 60 /// ditto 61 final void factory(T, alias F)(string name, Flag!"asSingleton" asSingleton = Yes.asSingleton) if (isCallable!F && is(ReturnType!F: T)) { 62 addProvider(typeid(T), name, maybeSingleton(new FactoryProvider!F(this), asSingleton)); 63 } 64 65 /// ditto 66 final void factory(T, F)(F func, Flag!"asSingleton" asSingleton = Yes.asSingleton) if ((is(F == delegate) || is(F == function)) && is(ReturnType!F: T)) { 67 addProvider(typeid(T), maybeSingleton(new FactoryProvider!F(this, func), asSingleton)); 68 } 69 /// ditto 70 final void factory(T, F)(string name, F func, Flag!"asSingleton" asSingleton = Yes.asSingleton) if ((is(F == delegate) || is(F == function)) && is(ReturnType!F: T)) { 71 addProvider(typeid(T), name, maybeSingleton(new FactoryProvider!F(this, func), asSingleton)); 72 } 73 74 /** 75 * Register a provider using the type returned by `provider.providedType`. 76 */ 77 final void provider(Provider provider) { 78 addProvider(provider.providedType, provider); 79 } 80 /// ditto 81 final void provider(string name, Provider provider) { 82 addProvider(provider.providedType, name, provider); 83 } 84 85 private Provider maybeSingleton(Provider provider, Flag!"asSingleton" asSingleton) pure nothrow { 86 if (asSingleton) { 87 return new SingletonProvider(provider); 88 } else { 89 return provider; 90 } 91 } 92 } 93 94 /** 95 * A simple implementation of `Container`. 96 * 97 * `SimpleContainer` stores providers keyed by the `TypeInfo` and name, in that order. 98 * The container itself is registered, so that it can be injected as a dependency into 99 * other objects. 100 */ 101 class SimpleContainer : Container { 102 103 pure this() { 104 addProvider(typeid(Container), new ValueProvider!Container(this)); 105 } 106 107 protected void addProvider(TypeInfo info, Provider provider) pure 108 out { 109 assert(info in providers); 110 assert(providers[info].mainProvider); 111 } 112 body { 113 auto providerGroup = info in providers; 114 if (providerGroup) { 115 providerGroup.mainProvider = provider; 116 } else { 117 providers[info] = ProviderGroup(provider); 118 } 119 } 120 121 protected void addProvider(TypeInfo info, string name, Provider provider) pure 122 out { 123 assert(info in providers); 124 assert(name in providers[info].namedProviders); 125 assert(providers[info].mainProvider); 126 } 127 body { 128 auto providerGroup = info in providers; 129 if (providerGroup) { 130 providerGroup.namedProviders[name] = provider; 131 } else { 132 providers[info] = ProviderGroup(provider, [name: provider]); 133 } 134 } 135 136 void withResolvedPtr(TypeInfo info, void delegate(void*) dg) { 137 auto providerGroup = info in providers; 138 assert(providerGroup, "Unable to resolve " ~ info.toString()); 139 providerGroup.mainProvider.withProvided(dg); 140 } 141 142 void withResolvedPtr(TypeInfo info, string name, void delegate(void*) dg) { 143 auto providerGroup = info in providers; 144 assert(providerGroup, "Unable to resolve " ~ info.toString()); 145 146 auto provider = name in providerGroup.namedProviders; 147 assert(provider, "Unable to resolve name " ~ name ~ " for type " ~ info.toString()); 148 149 return provider.withProvided(dg); 150 } 151 152 bool canResolve(TypeInfo info) { 153 return cast(bool) (info in providers); 154 } 155 156 bool canResolve(TypeInfo info, string name) { 157 auto providerGroup = info in providers; 158 return providerGroup && name in providerGroup.namedProviders; 159 } 160 161 private ProviderGroup[TypeInfo] providers; 162 163 private static struct ProviderGroup { 164 Provider mainProvider; 165 Provider[string] namedProviders; 166 } 167 } 168 169 /// 170 unittest { 171 import injected.injection; 172 173 @Injectable!(int, Injected!(int, "a")) 174 static class A { 175 int x; 176 int y; 177 this(int x, int y) { 178 this.x = x; 179 this.y = y; 180 } 181 } 182 static class B : A { 183 string name; 184 this(int x, int y, string name) { 185 super(x, y); 186 this.name = name; 187 } 188 } 189 static struct C { 190 string name; 191 ushort id; 192 static ushort idCounter = 0; 193 } 194 static C makeC() { 195 return C("Foo", C.idCounter++); 196 } 197 auto container = makeContainer(); 198 container.value!int(0); 199 container.value!int("a", 1); 200 container.register!A(); 201 container.register!(A, B)("b"); 202 container.factory!string(delegate(C c) { 203 return c.name; 204 }); 205 container.factory!(C, makeC)(); 206 container.factory!(C, makeC)("c", No.asSingleton); 207 208 assert(container.resolve!int() == 0); 209 assert(container.resolve!int("a") == 1); 210 assert(container.resolve!string() == "Foo"); 211 auto a = container.resolve!A(); 212 assert(a.x == 0); 213 assert(a.y == 1); 214 auto b = cast(B) container.resolve!A("b"); 215 assert(b); 216 assert(b.x == 0); 217 assert(b.y == 0); 218 assert(b.name == "Foo"); 219 assert(container.resolve!C().id == 0); 220 auto c = container.resolve!C("c"); 221 assert(c.name == "Foo"); 222 assert(c.id == 1); 223 assert(container.resolve!C("c").id == 2); 224 } 225 226 /** 227 * Make a new default container 228 */ 229 pure Container makeContainer() { 230 return new SimpleContainer(); 231 } 232 233 /** 234 * A dependency container that uses another container 235 * to resolve dependencies it doesn't know about itself. 236 */ 237 class DerivedContainer : SimpleContainer { 238 private Container _parent; 239 240 this(Container parent) { 241 _parent = parent; 242 } 243 244 @property Container parent() { 245 return _parent; 246 } 247 248 override void withResolvedPtr(TypeInfo info, void delegate(void*) dg) { 249 if (super.canResolve(info)) { 250 super.withResolvedPtr(info, dg); 251 } else { 252 _parent.withResolvedPtr(info, dg); 253 } 254 } 255 256 override void withResolvedPtr(TypeInfo info, string name, void delegate(void*) dg) { 257 if (super.canResolve(info, name)) { 258 super.withResolvedPtr(info, name, dg); 259 } else { 260 _parent.withResolvedPtr(info, name, dg); 261 } 262 } 263 264 override bool canResolve(TypeInfo info) { 265 return super.canResolve(info) || _parent.canResolve(info); 266 } 267 268 override bool canResolve(TypeInfo info, string name) { 269 return super.canResolve(info, name) || _parent.canResolve(info); 270 } 271 } 272 273 /// 274 unittest { 275 auto base = makeContainer(); 276 base.value!int(6); 277 base.value!string("foo"); 278 279 auto derived = base.derivedContainer(); 280 derived.value!string("bar"); 281 282 assert(derived.resolve!string() == "bar"); 283 assert(derived.resolve!int() == 6); 284 } 285 286 /** 287 * Create a derived container from a parent container. 288 */ 289 DerivedContainer derivedContainer(Container parent) { 290 return new DerivedContainer(parent); 291 }