1 module injected.provider; 2 3 import std.traits : ReturnType, Parameters, isCallable; 4 5 import injected.injection; 6 import injected.resolver : Resolver; 7 8 /** 9 * Interface for a provider for dependency injection. 10 * A provider knows about the type it produces, and 11 * can produce a value. 12 */ 13 interface Provider { 14 /** 15 * Produce the value. 16 * A pointer to the value is passed to a delegate. 17 * 18 * Notes: The pointer may no longer be valid after `dg` returns, so the value 19 * pointed to should be copied to persist it. 20 */ 21 void withProvided(void delegate(void*) dg); 22 23 /** 24 * Provied the value. 25 * T must be the same type represented by the `TypeInfo` 26 * returned by `providedType`. 27 */ 28 final T provide(T)() { 29 assert(typeid(T) == providedType()); 30 T result; 31 withProvided(delegate(ptr) { 32 result = *(cast(T*) ptr); 33 }); 34 return result; 35 } 36 37 /** 38 * Return a `TypeInfo` describing the type provided. 39 */ 40 @property TypeInfo providedType() const; 41 } 42 43 /** 44 * A `Provider` that provides a value. The value is 45 * provided at construction type and the same value 46 * is returned each time provide is called. 47 */ 48 class ValueProvider(T) : Provider { 49 T value; 50 51 this(T val) pure { 52 value = val; 53 } 54 55 void withProvided(void delegate(void*) dg) { 56 dg(&value); 57 } 58 59 @property TypeInfo providedType() const { 60 return typeid(T); 61 } 62 } 63 64 65 unittest { 66 Provider provider = new ValueProvider!int(10); 67 assert(provider.providedType == typeid(int)); 68 assert(provider.provide!int() == 10); 69 } 70 71 unittest { 72 class C {} 73 auto c = new C(); 74 auto provider = new ValueProvider!C(c); 75 assert(provider.providedType == typeid(C)); 76 assert(provider.provide!C() is c); 77 } 78 79 /** 80 * A Provider that uses another provider to create an instance 81 * the first time `provide` is called. Future calls to `provide` 82 * will return this same instance. 83 */ 84 class SingletonProvider : Provider { 85 private Provider _base; 86 private void* _instance; 87 88 this(Provider baseProvider) pure nothrow { 89 _base = baseProvider; 90 } 91 92 void withProvided(void delegate(void*) dg) { 93 synchronized (this) { 94 if (_instance is null) { 95 createInstance(); 96 } 97 } 98 dg(_instance); 99 } 100 101 @property TypeInfo providedType() const { 102 return _base.providedType(); 103 } 104 105 /** 106 * Create an instance using the base provider. 107 * 108 * Since we don't know if the value is allocated on the stack 109 * or the heap, we need to allocate space on the heap and copy it 110 * there. 111 */ 112 private void createInstance() { 113 import core.memory : GC; 114 import core.stdc.string : memcpy; 115 auto info = _base.providedType(); 116 _base.withProvided((ptr) { 117 _instance = GC.malloc(info.tsize, GC.getAttr(ptr), info); 118 memcpy(_instance, ptr, info.tsize); 119 }); 120 info.postblit(_instance); 121 } 122 } 123 124 unittest { 125 class BaseProvider : Provider { 126 private int counter = 0; 127 128 void withProvided(void delegate(void*) dg) { 129 dg(&counter); 130 } 131 132 @property TypeInfo providedType() const { 133 return typeid(int); 134 } 135 } 136 137 auto provider = new SingletonProvider(new BaseProvider); 138 assert(provider.providedType == typeid(int)); 139 int first = provider.provide!int(); 140 assert(first == 0); 141 assert(provider.provide!int()== 0); 142 } 143 144 /** 145 * A Provider that instantiates instances of a class. 146 * 147 * Arguments to the constructor are resolved using a `Resolver` (typically a `Container`). 148 * If an `Injectable` annotation is on the class, then the template arguments to the `Injectable` 149 * determine how the injected arguments should be resolved. Otherwise, the argument types for the 150 * first constructor are used. 151 */ 152 class ClassProvider(T) : Provider if (is(T == class)) { 153 private Resolver _resolver; 154 155 private alias Injectables = getInjectables!(T); 156 157 static if (Injectables.length > 0) { 158 private alias Injections = Injectables[0].Injections; 159 } else { 160 private alias Injections = Parameters!(__traits(getMember, T, "__ctor")); 161 } 162 163 this(Resolver resolver) pure { 164 _resolver = resolver; 165 } 166 167 void withProvided(void delegate(void*) dg) { 168 auto provided = new T(injectionSeq!(Injections)(_resolver).expand); 169 dg(&provided); 170 } 171 172 @property TypeInfo_Class providedType() const { 173 return typeid(T); 174 } 175 } 176 177 unittest { 178 import injected.container; 179 180 static class A {} 181 static class B {} 182 static class Test { 183 this(A a, B b, int c) { 184 this.a = a; 185 this.b = b; 186 this.c = c; 187 } 188 this(A a, B b) { 189 this.a = a; 190 this.b = b; 191 this.c = -1; 192 } 193 A a; 194 B b; 195 int c; 196 } 197 198 auto container = makeContainer(); 199 auto a = new A(); 200 auto b = new B(); 201 container.value!A(a); 202 container.value!B(b); 203 container.value!int(10); 204 205 auto provider = new ClassProvider!Test(container); 206 assert(provider.providedType == typeid(Test)); 207 208 auto test = provider.provide!Test(); 209 assert(test); 210 assert(test.a is a); 211 assert(test.b is b); 212 assert(test.c == 10); 213 } 214 215 unittest { 216 import injected.injection; 217 import injected.container; 218 219 @Injectable!(Injected!(int,"a"), Injected!(int, "b"), float) 220 static class Test { 221 this() { } 222 this(int a, int b, float c) { 223 this.a = a; 224 this.b = b; 225 this.c = c; 226 } 227 int a; 228 int b; 229 float c; 230 } 231 232 auto container = makeContainer(); 233 container.value!int("a", 5); 234 container.value!int("b", 10); 235 container.value!float(3.14); 236 237 auto provider = new ClassProvider!Test(container); 238 assert(provider.providedType == typeid(Test)); 239 240 auto test = provider.provide!Test(); 241 assert(test); 242 assert(test.a == 5); 243 assert(test.b == 10); 244 assert(test.c == 3.14f); 245 } 246 247 /** 248 * A provider that uses a factory function (or other callable) to 249 * get the value. 250 * 251 * Arguments to the function are resolved using a `Resolver` (typically a `Container`). 252 * If an `Injectable` annotation is on the definition of the function, then the template 253 * arguments to the `Injectable` determine how the injected arguments should be resolved. 254 * Otherwise, the argument types for the function are used. 255 * 256 * The function can either be passed as an alias function argument or as a parameter 257 * to the constructor, depending on the needs of the user. 258 */ 259 class FactoryProvider(alias F) : Provider if (isCallable!F) { 260 private Resolver _resolver; 261 262 private alias Injectables = getInjectables!(F); 263 static if (Injectables.length > 0) { 264 private alias Injections = Injectables[0].Injections; 265 } else { 266 private alias Injections = Parameters!F; 267 } 268 269 /** 270 * Parameters: `resolver` is used to resolve the arguments to the factory function 271 * which could be either be specified using an `Injectable` annotation, or inferred 272 * from the parameter list of the function. 273 */ 274 this(Resolver resolver) pure { 275 _resolver = resolver; 276 } 277 278 void withProvided(void delegate(void*) dg) { 279 auto value = F(injectionSeq!(Injections)(_resolver).expand); 280 dg(&value); 281 } 282 283 @property TypeInfo providedType() const { 284 return typeid(ReturnType!F); 285 } 286 } 287 288 unittest { 289 import injected.container; 290 static class A {} 291 auto a = new A(); 292 293 static struct Result { 294 A a; 295 int b; 296 string c; 297 int d; 298 } 299 300 auto container = makeContainer(); 301 container.value!int(6); 302 container.value!string("foo"); 303 container.value!A(a); 304 container.value!int("d", 1); 305 306 Result test1(A a, int b, string c) { 307 return Result(a, b, c); 308 } 309 310 @Injectable!(A, Injected!int, string, Injected!(int, "d")) 311 Result test2(A a, int b, string c, int d) { 312 return Result(a, b, c, d); 313 } 314 315 Provider provider = new FactoryProvider!test1(container); 316 assert(provider.providedType == typeid(Result)); 317 auto result = provider.provide!Result(); 318 assert(result.a is a); 319 assert(result.b == 6); 320 assert(result.c == "foo"); 321 assert(result.d == int.init); 322 323 provider = new FactoryProvider!test2(container); 324 assert(provider.providedType == typeid(Result)); 325 result = provider.provide!Result(); 326 assert(result.a is a); 327 assert(result.b == 6); 328 assert(result.c == "foo"); 329 assert(result.d == 1); 330 } 331 332 /// ditto 333 class FactoryProvider(F) : Provider if (is(F == function) || is(F == delegate)) { 334 private Resolver _resolver; 335 private F _func; 336 337 private alias Injections = Parameters!F; 338 339 /** 340 * Parameters: `resolver` is used to resolve arguments, `func` is the factory function to use. 341 */ 342 this(Resolver resolver, F func) pure { 343 _resolver = resolver; 344 _func = func; 345 } 346 347 void withProvided(void delegate(void*) dg) { 348 auto value = _func(injectionSeq!(Injections)(_resolver).expand); 349 dg(&value); 350 } 351 352 @property TypeInfo providedType() const { 353 return typeid(ReturnType!F); 354 } 355 } 356 357 unittest { 358 import injected.container; 359 360 class A {} 361 auto a = new A(); 362 class B {} 363 auto b = new B(); 364 class C {} 365 auto c = new C(); 366 367 static struct Result { 368 A a; 369 B b; 370 C c; 371 } 372 373 auto container = makeContainer(); 374 container.value(a); 375 container.value(b); 376 container.value(c); 377 378 auto dg = delegate(A a, B b, C c) { 379 return Result(a, b, c); 380 }; 381 382 auto provider = new FactoryProvider!(typeof(dg))(container, dg); 383 assert(provider.providedType == typeid(Result)); 384 auto result = provider.provide!Result(); 385 386 assert(result.a is a); 387 assert(result.b is b); 388 assert(result.c is c); 389 }