1 /**
2  * Contains the implementation of the provider.
3  *
4  * Copyright: (c) 2015-2020, Milofon Project.
5  * License: Subject to the terms of the BSD 3-Clause License, as written in the included LICENSE.md file.
6  * Author: <m.galanin@milofon.pro> Maksim Galanin
7  * Date: 2020-04-11
8  */
9 
10 module dango.inject.provider;
11 
12 private
13 {
14     import std.format : fmt = format;
15     import std.exception : enforce;
16     import std.meta : anySatisfy, AliasSeq, Filter;
17     import std.traits;
18 
19     import dango.inject.container: DependencyContainer;
20     import dango.inject.factory : ComponentFactory;
21     import dango.inject.exception : InjectDangoException;
22     import dango.inject.injection : Inject, Named, inject;
23 }
24 
25 
26 /**
27  * Dependency registration
28  */
29 class Registration
30 {
31     private 
32     {
33         Provider _provider;
34         bool _isNamed;
35         string _name;
36     }
37 
38     /**
39      * Main constructor
40      */
41     this(Provider provider) nothrow @safe
42     {
43         this._provider = provider;
44         this._isNamed = false;
45     }
46 
47     /**
48      * Main constructor with name
49      */
50     this(Provider provider, string name) nothrow @safe
51     {
52         this._provider = provider;
53         this._name = name;
54         this._isNamed = true;
55     }
56 
57     /**
58      * Return a `TypeInfo` describing the type provided.
59      */
60     TypeInfo providedType() const pure nothrow @safe
61     {
62         return _provider.providedType();
63     }
64 
65     /**
66      * Return a `TypeInfo` describing the type registered.
67      */
68     TypeInfo registeredType() const pure nothrow @safe
69     {
70         return _provider.registeredType();
71     }
72 
73     /**
74      * Return a `string` describing the name.
75      */
76     string name() const pure nothrow @safe
77     {
78         return _name;
79     }
80 
81     /**
82      * Return isNamed registration.
83      */
84     bool isNamed() const pure nothrow @safe
85     {
86         return _isNamed;
87     }
88 
89 
90     T provide(T)(bool injectInstance) @safe
91     {
92         T result;
93         _provider.withProvided(injectInstance, (val) @trusted {
94                 static if (is(T == interface) || is(T == class))
95                     result = cast(T)(*(cast(Object*)val));
96                 else
97                     result = *(cast(T*)val);
98             });
99         return result;
100     }
101 
102     /**
103      * Return maybe singleton registration;
104      */
105     bool canSingleton() const pure nothrow @safe
106     {
107         return _provider.canSingleton();
108     }
109 }
110 
111 
112 /**
113  * Interface for a provider for dependency injection.
114  * A provider knows about the type it produces, and
115  * can produce a value.
116  */
117 interface Provider
118 {
119     /**
120      * Return a `TypeInfo` describing the type provided.
121      */
122     TypeInfo providedType() const pure nothrow @safe;
123 
124     /**
125      * Return a `TypeInfo` describing the type registered.
126      */
127     TypeInfo registeredType() const pure nothrow @safe;
128 
129     /**
130      * Produce the value.
131      * A pointer to the value is passed to a delegate.
132      *
133      * Notes: The pointer may no longer be valid after `dg` returns, so the value
134      * pointed to should be copied to persist it.
135      */
136     void withProvided(bool injectInstance, void delegate(void*) @safe dg) @safe;
137 
138     /**
139      * Return maybe singleton provider
140      */
141     bool canSingleton() const pure nothrow @safe;
142 
143     /**
144      * Return original provider
145      */
146     Provider originalProvider() pure nothrow @safe;
147 }
148 
149 
150 /**
151  * A `Provider` that provides a value. The value is
152  * provided at construction type and the same value
153  * is returned each time provide is called.
154  */
155 class ValueProvider(T) : Provider
156 {
157     private T _value;
158 
159     /**
160      * Common constructor
161      */
162     this(T value) nothrow @safe
163     {
164         this._value = value;
165     }
166 
167     /**
168      * Return a `TypeInfo` describing the type provided.
169      */
170     TypeInfo providedType() const pure nothrow @safe
171     {
172         return typeid(T);
173     }
174 
175     /**
176      * Return a `TypeInfo` describing the type registered.
177      */
178     TypeInfo registeredType() const pure nothrow @safe
179     {
180         return typeid(T);
181     }
182 
183     /**
184      * Produce the value.
185      * A pointer to the value is passed to a delegate.
186      *
187      * Notes: The pointer may no longer be valid after `dg` returns, so the value
188      * pointed to should be copied to persist it.
189      */
190     void withProvided(bool injectInstance, void delegate(void*) @safe dg) @safe
191     {
192         dg(&_value);
193     }
194 
195     /**
196      * Return maybe singleton provider
197      */
198     bool canSingleton() const pure nothrow @safe
199     {
200         return false;
201     }
202 
203     /**
204      * Return original provider
205      */
206     Provider originalProvider() pure nothrow @safe
207     {
208         return this;
209     }
210 }
211 
212 @("Should work value provider")
213 @system unittest
214 {
215     auto provider = new ValueProvider!int(10);
216     assert (provider.providedType == typeid(int));
217     assert (provider._value == 10);
218 }
219 
220 @("Should work value provider existing class")
221 @system unittest
222 {
223     class C {}
224     auto c = new C();
225     auto provider = new ValueProvider!C(c);
226     assert (provider.providedType == typeid(C));
227     assert (provider._value is c);
228 }
229 
230 
231 /**
232  * A provider that uses a factory to get the value.
233  */
234 class FactoryProvider(F : ComponentFactory!(T, A), T, A...) : Provider
235 {   
236     private 
237     {
238         DependencyContainer _container;
239         F _factory;
240         A _args;
241     }
242 
243     /**
244      * Main constructor
245      */
246     this(DependencyContainer container, F factory, A args)
247     {
248         this._container = container;
249         this._factory = factory;
250         this._args = args;
251     }
252 
253     /**
254      * Return a `TypeInfo` describing the type provided.
255      */
256     TypeInfo providedType() const nothrow @safe
257     {
258         return typeid(T);
259     }
260 
261     /**
262      * Return a `TypeInfo` describing the type registered.
263      */
264     TypeInfo registeredType() const pure nothrow @safe
265     {
266         return typeid(T);
267     }
268 
269     /**
270      * Produce the value.
271      * A pointer to the value is passed to a delegate.
272      *
273      * Notes: The pointer may no longer be valid after `dg` returns, so the value
274      * pointed to should be copied to persist it.
275      */
276     void withProvided(bool injectInstance, void delegate(void*) @safe dg) @trusted
277     {
278         static if (is(T == class) || is (T == interface))
279         {
280             T instance = _factory.createComponent(_args);
281             Object result = cast(Object)instance;
282         }
283         else
284             T result = _factory.createComponent(_args);
285 
286         dg(&result);
287     }
288 
289     /**
290      * Return maybe singleton provider
291      */
292     bool canSingleton() const pure nothrow @safe
293     {
294         return false;
295     }
296 
297     /**
298      * Return original provider
299      */
300     Provider originalProvider() pure nothrow @safe
301     {
302         return this;
303     }
304 }
305 
306 @("Should work factory provider")
307 @system unittest
308 {
309     interface Animal { string name() @safe; }
310     class Cat : Animal { 
311         private string _name;
312         this(string name) @safe { _name = name; }
313         string name() @safe { return _name; }
314     }
315     class TestFactory : ComponentFactory!(Animal, string)
316     {
317         Animal createComponent(string name) @safe
318         {
319             return new Cat(name);
320         }
321     }
322 
323     auto container = new DependencyContainer();
324     auto factory = new TestFactory();
325     Provider provider = new FactoryProvider!(TestFactory)(container, factory, "cat");
326     assert (provider.providedType == typeid(Animal));   
327     auto reg = new Registration(provider);
328     auto result = reg.provide!Animal(false);
329     assert (result.name == "cat");
330 }
331 
332 
333 /**
334  * A Provider that instantiates instances of a class.
335  *
336  * Arguments to the constructor are resolved using a `Resolver` (typically a `Container`).
337  * If an `Injectable` annotation is on the class, then the template arguments to the `Injectable`
338  * determine how the injected arguments should be resolved. Otherwise, the argument types for the
339  * first constructor are used.
340  */
341 class ClassProvider(I, T : I) : Provider
342     if (is(T == class))
343 {
344     private 
345     {
346         DependencyContainer _container;
347         bool isBeingInjected;
348     }
349 
350     /**
351      * Main constructor
352      */
353     this(DependencyContainer container) @safe nothrow
354     {
355         this._container = container;
356     }
357 
358     /**
359      * Return a `TypeInfo` describing the type provided.
360      */
361     TypeInfo providedType() const pure nothrow @safe
362     {
363         return typeid(T);
364     }
365 
366     /**
367      * Return a `TypeInfo` describing the type registered.
368      */
369     TypeInfo registeredType() const pure nothrow @safe
370     {
371         return typeid(I);
372     }
373 
374     /**
375      * Produce the value.
376      * A pointer to the value is passed to a delegate.
377      *
378      * Notes: The pointer may no longer be valid after `dg` returns, so the value
379      * pointed to should be copied to persist it.
380      */
381     void withProvided(bool injectInstance, void delegate(void*) @safe dg) @trusted
382     {
383         enforce!InjectDangoException(_container,
384                 "A dependency container is not defined. Cannot perform constructor" ~ 
385                     " injection without one.");
386         enforce!InjectDangoException(!isBeingInjected,
387                 fmt!("%s is already being created and injected; possible circular" ~ 
388                     "dependencies in constructors?")(T.stringof));
389 
390         V resolveMember(V, P...)()
391         {
392             enum IsNamed(alias N) = is(typeof(N) == Named);
393             enum named = Filter!(IsNamed, P);
394             static if (named.length)
395             {
396                 enum name = named[0].name;
397                 static if (isDynamicArray!V && !isSomeString!V)
398                     return _container.resolveAll!(ForeachType!V)(name);
399                 else
400                     return _container.resolve!V(name);
401             }
402             else
403             {
404                 static if (isDynamicArray!V && !isSomeString!V)
405                     return _container.resolveAll!(ForeachType!V);
406                 else
407                     return _container.resolve!V;
408             }
409         }
410 
411         template IsCtorInjected(alias ctor)
412         {
413             template IsInjectAttribute(alias A)
414             {
415                 enum IsInjectAttribute = (is(A == Inject!Q, Q) ||
416                     __traits(isSame, A, Inject));
417             }
418             enum IsCtorInjected = anySatisfy!(IsInjectAttribute,
419                     __traits(getAttributes, ctor));
420         }
421 
422         T instance = null;
423         static if (__traits(compiles, __traits(getOverloads, T, `__ctor`)))
424         {
425             foreach(ctor ; __traits(getOverloads, T, `__ctor`))
426             {
427                 static if (IsCtorInjected!ctor)
428                 {
429                     enum CtorLen = __traits(getAttributes, ctor).length;
430                     alias Params = Parameters!ctor;
431                     isBeingInjected = true;
432                     scope(exit) 
433                         isBeingInjected = false;
434                     Params params = void;
435                     foreach (i, param; Params)
436                     {
437                         alias SP = Params[i..i+1];
438                         static if (__traits(compiles, __traits(getAttributes, SP)))
439                         {
440                             alias Attr = AliasSeq!(__traits(getAttributes, SP));
441                             static if (Attr.length)
442                                 params[i] = resolveMember!(param, Attr[CtorLen..Attr.length]);
443                             else
444                                 params[i] = resolveMember!(param, Attr);
445                         }
446                         else
447                             params[i] = resolveMember!(param);
448                     }
449                     instance = new T(params);
450                     break;
451                 }
452             }
453         }
454 
455         if (instance is null)
456             instance = cast(T)(typeid(T).create());
457 
458         enforce!InjectDangoException(instance !is null,
459                 "Unable to create instance of type" ~ T.stringof ~
460                     ", does it have injectable constructors?");
461 
462         if (injectInstance)
463             _container.inject!T(instance);
464 
465         dg(&instance);
466     }
467 
468     /**
469      * Return maybe singleton provider
470      */
471     bool canSingleton() const pure nothrow @safe
472     {
473         return true;
474     }
475 
476     /**
477      * Return original provider
478      */
479     Provider originalProvider() pure nothrow @safe
480     {
481         return this;
482     }
483 }
484 
485 
486 version (unittest)
487 {
488     import std.exception : assertThrown; 
489     interface Printer { string name() @safe; }
490     class LaserPrinter : Printer { 
491         string name() @safe { return "LaserJet 2000"; }
492     }
493     struct Page {}
494     interface Report { 
495         string print() @safe; 
496         Page[] pages() @safe;
497         string title() @safe;
498     }
499     abstract class BaseReport : Report
500     {
501         Page[] pages() @safe { return []; }
502         string title() @safe { return "t"; }
503     }
504     class SimpleReport : BaseReport { 
505         string print() @safe { return "simple"; }
506     }
507     class ErrorReport : BaseReport {
508         @Inject
509         this(Report parent) @safe {}
510         string print() @safe { return "error"; }
511     }
512     class AdditionalReport : BaseReport {
513         private {
514             Printer _printer;
515             string _title;
516             Page[] _pages;
517         }
518         @Inject
519         this(Printer printer, Page[] pages, @Named("title") string title)
520         {
521             this._printer = printer;
522             this._title = title;
523             this._pages = pages;
524         }
525         string print() @safe { return _printer.name; }
526         override Page[] pages() @safe { return _pages; }
527         override string title() @safe { return _title; }
528     }
529 }
530 
531 @("Should work register concrete named class")
532 @system unittest
533 {
534     auto cont = new DependencyContainer();
535     auto reg1 = cont.register!SimpleReport("r1");
536     auto reg2 = cont.register!SimpleReport("r2");
537     assert (reg1 != reg2);
538     assertThrown(cont.resolve!Report);
539     auto reps = cont.resolveAll!SimpleReport;
540     assert (reps.length == 2);
541     auto report = cont.resolve!SimpleReport("r1");
542     assert (report && report.print == "simple");
543 }
544 
545 @("Should work resolve class by circular constructor")
546 @system unittest
547 {
548     auto cont = new DependencyContainer();
549     cont.register!(Report, ErrorReport);
550     assertThrown(cont.resolve!Report);
551 }
552 
553 @("Should work resolve class by constructor")
554 @system unittest
555 {
556     auto cont = new DependencyContainer();
557     cont.register!(Printer, LaserPrinter);
558     cont.value("p1", Page());
559     cont.value("p2", Page());
560     cont.value("title", "printer");
561     cont.register!(Report, AdditionalReport);
562     auto rep = cont.resolve!(Report);
563     assert (rep);
564     assert (rep.print == "LaserJet 2000");
565     assert (rep.pages.length == 2);
566     assert (rep.title == "printer");
567 }
568 
569 
570 /**
571  * A Provider that uses another provider to create an instance
572  * the first time `provide` is called. Future calls to `provide`
573  * will return this same instance.
574  */
575 class SingletonProvider : Provider
576 {
577     private
578     {
579         Provider _original;
580         void* _instance;
581     }
582 
583     /**
584      * Common constructor
585      */
586     this(Provider orig) pure nothrow @safe
587     {
588         this._original = orig;
589     }
590 
591     /**
592      * Return a `TypeInfo` describing the type provided.
593      */
594     TypeInfo providedType() const pure nothrow @safe
595     {
596         return _original.providedType();
597     }
598 
599     /**
600      * Return a `TypeInfo` describing the type registered.
601      */
602     TypeInfo registeredType() const pure nothrow @safe
603     {
604         return _original.registeredType();
605     }
606 
607     /**
608      * Produce the value.
609      * A pointer to the value is passed to a delegate.
610      *
611      * Notes: The pointer may no longer be valid after `dg` returns, so the value
612      * pointed to should be copied to persist it.
613      */
614     void withProvided(bool injectInstance, void delegate(void*) @safe dg) @safe
615     {
616         if (_instance is null)
617         {
618             synchronized (this)
619             {
620                 if (_instance is null)
621                     createInstance(injectInstance);
622             }
623         }
624         dg(_instance);
625     }
626 
627     /**
628      * Return maybe singleton provider
629      */
630     bool canSingleton() const pure nothrow @safe
631     {
632         return false;
633     }
634 
635     /**
636      * Return original provider
637      */
638     Provider originalProvider() pure nothrow @safe
639     {
640         return _original;
641     }
642 
643     /**
644      * Create an instance using the base provider.
645      *
646      * Since we don't know if the value is allocated on the stack
647      * or the heap, we need to allocate space on the heap and copy it
648      * there.
649      */
650     private void createInstance(bool injectInstance) @trusted
651     {
652         import core.memory : GC;
653         import core.stdc.string : memcpy;
654         auto info = _original.providedType();
655         _original.withProvided(injectInstance, (void* ptr) @trusted {
656                 if (ptr !is null)
657                 {
658                     _instance = GC.malloc(info.tsize, GC.getAttr(ptr), info);
659                     memcpy(_instance, ptr, info.tsize);
660                 }
661             });
662         info.postblit(_instance);
663     }
664 }
665 
666 @("Should work SingletonProvider")
667 @system unittest
668 {
669     class BaseProvider : Provider
670     {
671         private int counter = 0;
672 
673         TypeInfo providedType() const pure nothrow @safe
674         {
675             return typeid(int);
676         }
677 
678         TypeInfo registeredType() const pure nothrow @safe
679         {
680             return typeid(int);
681         }
682 
683         void withProvided(bool ii, void delegate(void*) @safe dg) @safe
684         {
685             counter++;
686             dg(&counter);
687         }
688 
689         bool canSingleton() const pure nothrow @safe
690         {
691             return false;
692         }
693 
694         Provider originalProvider() pure nothrow @safe
695         {
696             return this;
697         }
698     }
699 
700     auto provider = new SingletonProvider(new BaseProvider());
701     assert (provider.providedType == typeid(int));
702     int first;
703     provider.withProvided(false, (void* val) @trusted {
704             first = *(cast(int*)val);
705         });
706     assert (first == 1);
707     provider.withProvided(false, (void* val) @trusted {
708             first = *(cast(int*)val);
709         });
710     assert (first == 1);
711 }
712 
713 
714 /**
715  * Scopes registrations to return the same instance every time a given registration is resolved.
716  *
717  * Effectively makes the given registration a singleton.
718  */
719 Registration singleInstance(Registration registration) nothrow @safe
720 {
721     if (registration.canSingleton)
722         registration._provider = new SingletonProvider(registration._provider);
723     return registration;
724 }
725 
726 
727 /**
728  * A Provider that uses another provider to existing instance
729  */
730 class ExistingInstanceProvider(I) : Provider
731     if (is(I == class))
732 {
733     private
734     {
735         Provider _original;
736         I _instance;
737     }
738 
739     /**
740      * Common constructor
741      */
742     this(I instance, Provider original) pure nothrow @safe
743     {
744         this._original = original;
745         this._instance = instance;
746     }
747 
748     /**
749      * Return a `TypeInfo` describing the type provided.
750      */
751     TypeInfo providedType() const pure nothrow @safe
752     {
753         return _original.providedType();
754     }
755 
756     /**
757      * Return a `TypeInfo` describing the type registered.
758      */
759     TypeInfo registeredType() const pure nothrow @safe
760     {
761         return _original.registeredType();
762     }
763 
764     /**
765      * Produce the value.
766      * A pointer to the value is passed to a delegate.
767      *
768      * Notes: The pointer may no longer be valid after `dg` returns, so the value
769      * pointed to should be copied to persist it.
770      */
771     void withProvided(bool injectInstance, void delegate(void*) @safe dg) @safe
772     {
773         dg(&_instance);
774     }
775 
776     /**
777      * Return maybe singleton provider
778      */
779     bool canSingleton() const pure nothrow @safe
780     {
781         return false;
782     }
783 
784     /**
785      * Return original provider
786      */
787     Provider originalProvider() pure nothrow @safe
788     {
789         return _original;
790     }
791 }
792 
793 
794 /**
795  * Scopes registrations to return a new instance every time the given registration is resolved.
796  */
797 Registration newInstance(Registration registration) nothrow @safe
798 {
799     registration._provider = registration._provider.originalProvider();
800     return registration;
801 }
802 
803 @("Should work singleInstance and newInstance")
804 @safe unittest
805 {
806     class Counter { private int counter; }
807     auto cont = new DependencyContainer();
808     auto reg = cont.register!(Counter).singleInstance;
809     auto cnt = cont.resolve!Counter;
810     cnt.counter++;
811     cnt = cont.resolve!Counter;
812     assert (cnt.counter == 1);
813     reg.newInstance; 
814     cnt = cont.resolve!Counter;
815     assert (cnt.counter == 0);
816 }
817 
818 
819 /**
820  * Scopes registrations to return the given instance every time the given registration is resolved.
821  */
822 Registration existingInstance(T)(Registration registration, T instance) @trusted
823     if (is(T == class))
824 {
825     if (!canBuiltinInheritance!T(registration.providedType))
826         throw new InjectDangoException(fmt!"Type '%s' not provide '%s'"(
827                     registration.providedType, typeid(T)));
828 
829     if (!registration.canSingleton)
830         throw new InjectDangoException(fmt!"Type '%s' not support singleton"(
831                     registration.providedType));
832 
833     registration._provider = new ExistingInstanceProvider!T(instance, registration._provider);
834     return registration;
835 }
836 
837 @("Should work existingInstance")
838 @safe unittest
839 {
840     class Counter { 
841         private int _counter;
842         this(int c) { _counter = c; }
843     }
844     auto cont = new DependencyContainer();
845     auto counter = new Counter(3);
846     cont.register!(Counter).existingInstance(counter);
847     auto rcnt = cont.resolve!Counter;
848     assert (rcnt._counter == 3);
849 }
850 
851 
852 /**
853  * Check TypeInfo
854  */
855 private bool canBuiltinInheritance(T)(TypeInfo info) nothrow @trusted
856     if (is(T == class))
857 {
858     import std.traits : BaseClassesTuple, InterfacesTuple;
859     import std.meta : AliasSeq, staticMap, Filter, staticIndexOf;
860 
861     template ToTypeInfo(A)
862     {
863         enum ToTypeInfo = typeid(A);
864     }
865 
866     template IsTypeInfo(A)
867     {
868         enum IsTypeInfo = !is(A == Object);
869     }
870 
871     enum Infos = staticMap!(ToTypeInfo, T, Filter!(IsTypeInfo,
872             AliasSeq!(BaseClassesTuple!T, InterfacesTuple!T)));
873 
874     try
875         foreach (typeInfo; Infos)
876         {
877             if (typeInfo == info)
878                 return true;
879         }
880     catch (Exception e)
881         return false;
882 
883     return false;
884 }
885 
886 @("Should work canBuiltinInheritance")
887 @safe unittest
888 {
889     interface TestT {}
890     interface TestA {}
891     interface TestB : TestA {}
892     abstract class TestC : TestB {}
893     class TestD : TestC {}
894     class TestE : TestD {}
895     assert (!canBuiltinInheritance!TestE(typeid(int)));
896     assert (canBuiltinInheritance!TestE(typeid(TestA)));
897 }
898