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 }