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 }