1 /**
2  * Promise range tools.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <ae@cy.md>
12  */
13 
14 module ae.utils.promise.range;
15 
16 import std.range.primitives;
17 
18 import ae.net.asockets : socketManager;
19 import ae.sys.timing;
20 import ae.utils.promise;
21 
22 /// Given a range of promises, resolve them one after another,
23 /// and return a promise which is fulfilled when all promises in `range` are fulfilled.
24 /// `range` may be a lazy range (e.g. a `map` which produces promises from other input),
25 /// which will cause the work to be started only when the previous promise completes.
26 PromiseValueTransform!(ElementType!R, x => [x]) allSerial(R)(R range)
27 if (isInputRange!R)
28 {
29 	auto p = new typeof(return);
30 
31 	alias P = ElementType!R;
32 	alias T = PromiseValue!P;
33 	alias E = PromiseError!P;
34 
35 	typeof(p).ValueTuple results;
36 	static if (!is(T == void))
37 	{
38 		static if (hasLength!R)
39 			results[0].reserve(range.length);
40 	}
41 
42 	void next()
43 	{
44 		if (range.empty)
45 			p.fulfill(results);
46 		else
47 		{
48 			range.front.then((P.ValueTuple value) {
49 				static if (!is(T == void))
50 					results[0] ~= value[0];
51 				next();
52 			}, (E error) {
53 				p.reject(error);
54 			});
55 			range.popFront();
56 		}
57 	}
58 
59 	next();
60 
61 	return p;
62 }
63 
64 unittest
65 {
66 	import std.algorithm.iteration : map;
67 	size_t sum;
68 	[1, 2, 3]
69 		.map!((n) {
70 			auto nextSum = sum + n;
71 			auto p = new Promise!void();
72 			setTimeout({ sum = nextSum; p.fulfill(); }, 0.seconds);
73 			return p;
74 		})
75 		.allSerial;
76 	socketManager.loop();
77 	assert(sum == 6);
78 }