1 /**
2   Copyright: 2017 © LLC CERERIS
3   License: MIT
4   Authors: LLC CERERIS
5 */
6 
7 module dich.container;
8 
9 import dich.binding;
10 import dich.exception;
11 import dich.provider;
12 import dich.reuse;
13 import std.algorithm;
14 import std.array;
15 import std.traits;
16 
17 /// Provides DI entity. Represents a container to register instances with provider
18 class Container
19 {
20   public:
21     /// Creates a Container object
22     this()
23     {
24       bindReuse!Transient;
25       bindReuse!Singleton;
26     }
27 
28 	/** Register reuse interfaces
29 	
30 	  Params:
31 		Class = Class instance to register reuse interface (Transient, Singleton)
32     */
33     void bindReuse(Class)()
34     {
35       immutable key = fullyQualifiedName!Class;
36       assert(key.length > 0);
37       if (key in _scopes)
38       {
39         throw new Exception("Scope already bound");
40       }
41       _scopes[key] = new Class();
42     }
43 
44 	/** Register reuse objects
45 	
46 	  Params:
47 	    R = Reuse interface (Transient, Singleton)
48 		instance = Object of C class to register in container
49 		
50 	  Examples:
51         ---
52         class MyClass{};
53         
54 		auto container = new Container();
55 		auto myClass = new MyClass();
56 		container.register!(MyClass)(myClass);
57 		---
58     */
59     void register(C, R: ReuseInterface = Transient)(C instance, string name = "")
60     {
61       register!(C, R)(new InstanceProvider(instance), name);
62     }
63 
64 	/** Register reuse objects through InstanceProvider
65 	
66 	  Params:
67 	    I = Interface type
68 	    R = Reuse interface (Transient, Singleton)
69 		provider = Object of InstanceProvider class to register in container
70 		
71 	  Examples:
72         ---
73         interface MyInterface{};
74         class MyClass: MyInterface{};
75         
76 		auto container = new Container();
77 		auto myClassInstance = new InstanceProvider(new MyClass());
78 		container.register!(MyInterface)(myClassInstance);
79 		---
80     */
81     void register(I, R: ReuseInterface = Transient)(ProviderInterface provider)
82     {
83       this.register!(I, R)(provider, "");
84     }
85 
86 	/** Register reuse objects through InstanceProvider specifying its name
87 	
88 	  Params:
89 	    I = Interface type
90 	    R = Reuse interface (Transient, Singleton)
91 		provider = Object of InstanceProvider class to register in container
92 		name = A string which allows to get instances by its name
93 		
94 	  Examples:
95         ---
96         interface MyInterface{};
97         class MyClass: MyInterface{};
98         
99 		auto container = new Container();
100 		auto myClassInstance = new InstanceProvider(new MyClass());
101 		container.register!(MyInterface)(myClassInstance, "My best class");
102 		---
103     */
104     void register(I, R: ReuseInterface = Transient)(ProviderInterface provider, string name)
105     {
106       if(exists!I(name))
107       {
108         throw new RegistrationException("Interface already bound!", fullyQualifiedName!I);
109       }
110 
111       _bindings ~= createBinding!(I, R)(provider, name);
112     }
113 
114 	/** Get reuse objects by its name
115 	
116 	  Params:
117 	    I = Interface type
118 		name = Name of instance specified in register function
119 		
120 	  Examples:
121         ---
122         interface MyInterface{};
123         class MyClass: MyInterface{};
124         
125 		auto container = new Container();
126 		auto myClassInstance = new InstanceProvider(new MyClass());
127 		container.register!(MyInterface)(myClassInstance, "My best class");
128 		auto myClass = container.get!(MyInterface)("My best class");
129 		---
130 
131 	  Returns:
132 	    Instance as an interface type
133     */
134     I get(I)(string name)
135     {
136       Binding[] binding = filterExactly!I(name);
137       if(binding.empty)
138       {
139         throw new ResolveException("Type is not registered!", fullyQualifiedName!I, name);
140       }
141 
142       return resolve!I(binding[0]);
143     }
144 
145 	/** Get reuse objects by its class
146 	  Params:
147 	    T = Interface type
148 	  
149 	  Examples:
150         ---
151         interface MyInterface{};
152         class MyClass: MyInterface{};
153         
154 		auto container = new Container();
155 		auto myClassInstance = new InstanceProvider(new MyClass());
156 		container.register!(MyInterface)(myClassInstance)
157 		auto myClass = container.get!(MyInterface)();
158 		---
159 
160 	  Returns:
161 	    Instance as an interface type
162     */
163     T get(T)()
164     {
165       // If want get array
166       static if(is(T t == I[], I))
167       {
168         // TODO: make array get
169       }
170       else
171       {
172         Binding[] bindings = filterExactly!T("");
173         if(bindings.empty)
174         {
175           throw new ResolveException("Type is not registered!", fullyQualifiedName!T);
176         }
177 
178         return resolve!T(bindings[0]);
179       }
180     }
181 
182   private:
183     bool exists(I)(string name)
184     {
185       return !filterExactly!I(name).empty;
186     }
187 
188     Binding[] filterExactly(I)(string name)
189     {
190       string requestedFullyQualifiedName = fullyQualifiedName!I;
191       return _bindings.filter!(a => a.fullyQualifiedName == requestedFullyQualifiedName && a.name == name).array;
192     }
193 
194     I resolve(I)(Binding binding)
195     {
196       string key = fullyQualifiedName!I;
197       if(binding.name.length > 0)
198       {
199         key ~= binding.name;
200       }
201 
202       return cast(I) binding.reuse.get(key, binding.provider);
203     }
204 
205     Binding createBinding(I, R)(ProviderInterface provider, string name)
206     {
207       auto reuse = _scopes[fullyQualifiedName!R];
208       return Binding(fullyQualifiedName!I, name, provider, reuse);
209     }
210 
211   private:
212     Binding[] _bindings;
213     ReuseInterface[string] _scopes;
214 }