1 /*
2 Copyright (c) 2019 Victor Porton,
3 Pure Dependency Injector - http://freesoft.portonvictor.org
4 
5 This file is part of Pure Dependency Injector.
6 
7 Pure Dependency Injector is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Affero General Public License as
9 published by the Free Software Foundation, either version 3 of the
10 License, or (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU Affero General Public License for more details.
16 
17 You should have received a copy of the GNU Affero General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 module pure_dependency.providers;
22 
23 import std.typecons;
24 import std.traits;
25 import memoize;
26 import struct_params;
27 
28 /**
29 Base class for non-reference providers.
30 
31 * `Result_` the type of the object to be provided.
32 
33 * `Params_` the type of provider parameters.
34 */
35 class Provider(Result_, Params_...) {
36     alias Result = Result_; /// the type of the object to be provided.
37     alias Params = Params_; /// the type of provider parameters.
38     /// Call the provider.
39     final Result opCall(Params params) {
40         return delegate_(params);
41     }
42     /// Call it with a structure or class as the argument (expanding its members into arguments
43     /// in order).
44     final Result call(S)(S s) {
45         return callMemberFunctionWithParamsStruct!(this, "opCall", S)(s);
46     }
47     /// The abstract virtual function used to create the provided object.
48     abstract Result delegate_(Params params);
49     alias DelegateType = Result delegate (Params params);
50     /// Returns `delegate_` as a delegate.
51     final @property DelegateType provider() {
52         return &delegate_;
53     }
54 }
55 
56 /**
57 Base class for reference providers.
58 
59 * `Result_` the type of the object to be provided.
60 
61 * `Params_` the type of provider parameters.
62 */
63 class ReferenceProvider(Result_, Params_...) {
64     alias Result = Result_; /// the type of the object to be provided.
65     alias Params = Params_; /// the type of provider parameters.
66     /// Call the provider.
67     final ref Result opCall(Params params) {
68         return delegate_(params);
69     }
70     /// Call it with a structure or class as the argument (expanding its members into arguments
71     /// in order).
72     final ref Result call(S)(S s) const {
73         return callMemberFunctionWithParamsStruct!(this, "opCall", S)(s);
74     }
75     /// The abstract virtual function used to create the provided object.
76     abstract ref Result delegate_(Params params);
77     alias DelegateType = ref Result delegate (Params params);
78     /// Returns `delegate_` as a delegate.
79     final @property DelegateType provider() {
80         return &delegate_;
81     }
82 }
83 
84 //class ClassFactory(Result, Params...) : Provider!(Result, Params) {
85 //    override ref Result delegate_(Params params) {
86 //        return new Result(params);
87 //    }
88 //}
89 //
90 //class StructFactory(Result, Params...) : Provider!(Result, Params) {
91 //    override ref Result delegate_(Params params) {
92 //        return Result(params);
93 //    }
94 //}
95 
96 /**
97 Non-reference provider that calls some function to create the provided object.
98 
99 `Function` the function called to create the provided object.
100 */
101 class Callable(alias Function) : Provider!(ReturnType!Function, Parameters!Function) {
102     override ReturnType!Function delegate_(Params params) {
103         return Function(params);
104     }
105 }
106 
107 /**
108 Reference provider that calls some function to create the provided object.
109 
110 `Function` the function called to create the provided object.
111 */
112 class ReferenceCallable(alias Function) : ReferenceProvider!(ReturnType!Function, Parameters!Function) {
113     override ref ReturnType!Function delegate_(Params params) {
114         return Function(params);
115     }
116 }
117 
118 /**
119 Base class for non-reference singletons. Uses another provider to create the object.
120 
121 `Base` is the type of this another provider.
122 */
123 class BaseGeneralSingleton(Base) : Provider!(Base.Result, Base.Params) {
124     private Base _base;
125     /// Create the singleton with given base provider.
126     this(Base base) {
127         _base = base;
128     }
129     /// Get the base provider.
130     @property ref Base base() { return _base; }
131 }
132 
133 /**
134 Base class for reference singletons. Uses another provider to create the object.
135 
136 `Base` is the type of this another provider.
137 */
138 class ReferenceBaseGeneralSingleton(Base) : ReferenceProvider!(Base.Result, Base.Params) {
139     private Base _base;
140     /// Create the singleton with given base provider.
141     this(Base base) {
142         _base = base;
143     }
144     /// Get the base provider.
145     @property ref Base base() { return _base; }
146 }
147 
148 /**
149 Non-reference thread-unsafe singleton.
150 */
151 class ThreadUnsafeSingleton(Base) : BaseGeneralSingleton!Base {
152     this(Base base) { super(base); }
153     override Result delegate_(Params params) {
154         return noLockMemoizeMember!(Base, "delegate_")(base, params);
155     }
156 }
157 
158 /**
159 Reference thread-unsafe singleton.
160 */
161 class ReferenceThreadUnsafeSingleton(Base) : ReferenceBaseGeneralSingleton!Base {
162     this(Base base) { super(base); }
163     override ref Result delegate_(Params params) {
164             return referenceNoLockMemoizeMember!(Base, "delegate_")(base, params);
165     }
166 }
167 
168 /**
169 Non-reference thread-safe singleton.
170 */
171 class ThreadSafeSingleton(Base) : BaseGeneralSingleton!Base {
172     this(Base base) { super(base); }
173     override Result delegate_(Params params) {
174         return synchronizedMemoizeMember!(Base, "delegate_")(base, params);
175     }
176 }
177 
178 /**
179 Reference thread-safe singleton.
180 */
181 class ReferenceThreadSafeSingleton(Base) : ReferenceBaseGeneralSingleton!Base {
182     this(Base base) { super(base); }
183     override ref Result delegate_(Params params) {
184         return referenceSynchronizedMemoizeMember!(Base, "delegate_")(base, params);
185     }
186 }
187 
188 /**
189 Non-reference thread-local singleton.
190 */
191 class ThreadLocalSingleton(Base) : BaseGeneralSingleton!Base {
192     this(Base base) { super(base); }
193     override Result delegate_(Params params) {
194         synchronized {
195             return memoizeMember!(Base, "delegate_")(base, params);
196         }
197     }
198 }
199 
200 /**
201 Reference thread-local singleton.
202 */
203 class ReferenceThreadLocalSingleton(Base) : ReferenceBaseGeneralSingleton!Base {
204     this(Base base) { super(base); }
205     override ref Result delegate_(Params params) {
206         synchronized {
207             return referenceMemoizeMember!(Base, "delegate_")(base, params);
208         }
209     }
210 }
211 
212 /**
213 Non-refernce provider that always returns the same object.
214 
215 `obj` is the object returned by the provider.
216 */
217 class FixedObject(alias obj) : Provider!(typeof(obj)) {
218     override Result delegate_() const {
219         return obj;
220     }
221 }
222 
223 /**
224 Refernce provider that always returns the same object.
225 
226 `obj` is the object returned by the provider.
227 */
228 class ReferenceFixedObject(alias obj) : ReferenceProvider!(typeof(obj)) {
229     override ref Result delegate_() const {
230         return obj;
231     }
232 }
233 
234 // TODO: *CallableSingleton classes are of an inefficient implementation (holding a pointer).
235 
236 /**
237 Non-reference singleton provider that calls some function to create the provided object.
238 
239 `Function` the function called to create the provided object.
240 */
241 class ThreadUnsafeCallableSingleton(alias Function) : ThreadUnsafeSingleton!(Callable!Function) {
242     this() {
243         super(new Callable!Function);
244     }
245 }
246 
247 /**
248 Reference singleton provider that calls some function to create the provided object.
249 
250 `Function` the function called to create the provided object.
251 */
252 class ReferenceThreadUnsafeCallableSingleton(alias Function) : ReferenceThreadUnsafeSingleton!(ReferenceCallable!Function) {
253     this() {
254         super(new ReferenceCallable!Function);
255     }
256 }
257 
258 /**
259 Non-reference singleton provider that calls some function to create the provided object.
260 
261 `Function` the function called to create the provided object.
262 */
263 class ThreadSafeCallableSingleton(alias Function) : ThreadSafeSingleton!(Callable!Function) {
264     this() {
265         super(new Callable!Function);
266     }
267 }
268 
269 /**
270 Reference singleton provider that calls some function to create the provided object.
271 
272 `Function` the function called to create the provided object.
273 */
274 class ReferenceThreadSafeCallableSingleton(alias Function) : ReferenceThreadSafeSingleton!(ReferenceCallable!Function) {
275     this() {
276         super(new ReferenceCallable!Function);
277     }
278 }
279 
280 /**
281 Non-reference singleton provider that calls some function to create the provided object.
282 
283 `Function` the function called to create the provided object.
284 */
285 class ThreadLocalCallableSingleton(alias Function) : ThreadLocalSingleton!(Callable!Function) {
286     this() {
287         super(new Callable!Function);
288     }
289 }
290 
291 /**
292 Reference singleton provider that calls some function to create the provided object.
293 
294 `Function` the function called to create the provided object.
295 */
296 class ReferenceThreadLocalCallableSingleton(alias Function) : ReferenceThreadLocalSingleton!(ReferenceCallable!Function) {
297     this() {
298         super(new ReferenceCallable!Function);
299     }
300 }
301 
302 /**
303 Create provider with `defaults` of type `ParamsType.Regular` from a provider `Base`.
304 
305 `ParamsType` should be like a struct `S` created with
306 ```d
307 mixin StructParams!("S", int, "x", float, "y"); // see struct-params package
308 ```
309 
310 Then use it like this:
311 ```d
312 float calc(int x, float y) {
313     return x * y;
314 }
315 immutable S.WithDefaults myDefaults = { x: 3, y: 2.1 }; // or `immutable S.Regular myDefaults`
316 alias MyProvider = ProviderWithDefaults!(Callable!calc, S, myDefaults);
317 
318 immutable S.WithDefaults providerParams = { x: 2 }; // note y is default initialized to null
319 auto provider = new MyProvider;
320 assert(provider.callWithDefaults(providerParams) - 4.2 < 1e-6);
321 ```
322 */
323 class ProviderWithDefaults(Base, ParamsType, alias defaults) : Base {
324     Result callWithDefaults(ParamsType.WithDefaults params) {
325         return call(combine(params, defaults));
326     }
327 }
328 
329 unittest {
330     class C {
331         int v;
332         this(int a, int b) { v = a + b; }
333     }
334     auto cFactory = new Callable!((int a, int b) => new C(a, b));
335     assert(cFactory(1, 2).v == 3);
336 }
337 
338 unittest {
339     int x;
340     ref int f() { return x; }
341     auto cFactory = new ReferenceCallable!(f);
342     assert(&cFactory() == &x);
343 }
344 
345 unittest {
346     int obj = 3;
347     int f(int a, int b) { return a + b; }
348     ref int g() { return obj; }
349 
350     assert(new FixedObject!obj()() == obj);
351     assert(&new ReferenceFixedObject!obj()() == &obj);
352 
353     assert(new ThreadUnsafeCallableSingleton!f()(2, 5) == 7);
354     auto refSingleton = new ReferenceThreadUnsafeCallableSingleton!g();
355     assert(refSingleton() == refSingleton());
356 
357     assert(new ThreadSafeCallableSingleton!f()(2, 5) == 7);
358     auto refSingleton2 = new ReferenceThreadSafeCallableSingleton!g();
359     assert(refSingleton2() == refSingleton2());
360 
361     assert(new ThreadLocalCallableSingleton!f()(2, 5) == 7);
362     auto refSingleton3 = new ReferenceThreadLocalCallableSingleton!g();
363     assert(refSingleton3() == refSingleton3());
364 }
365 
366 unittest {
367     mixin StructParams!("S", int, "x", float, "y");
368     float calc(int x, float y) {
369         return x * y;
370     }
371     immutable S.Regular myDefaults = { x: 3, y: 2.1 };
372     alias MyProvider = ProviderWithDefaults!(Callable!calc, S, myDefaults);
373     immutable S.WithDefaults myDefaults2 = { x: 3, y: 2.1 };
374     alias MyProvider2 = ProviderWithDefaults!(Callable!calc, S, myDefaults2);
375 
376     immutable S.WithDefaults providerParams = { x: 2 }; // note y is default initialized to null
377     auto provider = new MyProvider;
378     auto provider2 = new MyProvider2;
379     import std.math : abs;
380     assert((provider.callWithDefaults(providerParams) - 4.2).abs < 1e-6);
381     assert((provider.callWithDefaults(providerParams) - 4.2).abs < 1e-6);
382 }
383