1 module upromised.operations;
2 import std.array : empty, front, popFront;
3 import std.range : isInputRange;
4 import upromised.promise : Promise, PromiseIterator;
5 
6 PromiseIterator!(typeof(T.init.front)) toAsync(T)(T input) nothrow
7 if (isInputRange!T)
8 {
9 	return new class PromiseIterator!(typeof(T.init.front)) {
10 		override Promise!ItValue next(Promise!bool) {
11 			if (input.empty) {
12 				return Promise!ItValue.resolved(ItValue(true));
13 			} else {
14 				auto value = ItValue(false, input.front);
15 				input.popFront;
16 				return Promise!ItValue.resolved(value);
17 			}
18 		}
19 	};
20 }
21 unittest {
22 	auto b = [1, 2, 3].toAsync;
23 	int calls = 0;
24 
25 	b.each((num) {
26 		assert(num == 1);
27 		assert(calls++ == 0);
28 		return false;
29 	}).then((eof) {
30 		assert(!eof);
31 		assert(calls++ == 1);	
32 	}).nothrow_().then(() {
33 		return b.each((num) {
34 			assert(num == calls++);
35 		});
36 	}).then((eof) {
37 		assert(eof);
38 		assert(calls++ == 4);
39 	}).nothrow_();
40 
41 	assert(calls == 5);
42 }
43 unittest {
44 	auto b = (new class Object {
45 		int front;
46 		void popFront() {
47 			front++;
48 		}
49 		bool empty() {
50 			return false;
51 		}
52 	}).toAsync;
53 
54 	int calls;
55 	b.each((num) {
56 		assert(calls++ == num);
57 		return num < 3;
58 	}).then((eof) {
59 		assert(!eof);
60 		assert(calls++ == 4);
61 	}).nothrow_();
62 
63 	assert(calls == 5);
64 }
65 PromiseIterator!(T[]) toAsyncChunks(T)(T[] input, size_t chunkLength = 1024) nothrow {
66 	import std.algorithm : min;
67 
68 	return new class PromiseIterator!(T[]) {
69 		override Promise!ItValue next(Promise!bool) {
70 			if (input.length == 0) {
71 				return Promise!ItValue.resolved(ItValue(true));
72 			} else {
73 				auto length = input.length.min(chunkLength);
74 				const auto value = input[0..length];
75 				input = input[length..$];
76 				return Promise!ItValue.resolved(ItValue(false, value));
77 			}
78 		}
79 	};
80 }
81 unittest {
82 	import std.array : join;
83 
84 	auto expected = ["abc", "def", "gh"];
85 	int calls = 0;
86 
87 	expected.join.toAsyncChunks(3).each((chunk) {
88 		assert(chunk == expected[calls]);
89 		calls++;
90 	}).then((eof) {
91 		assert(eof);
92 		assert(calls++ == 3);
93 	}).nothrow_();
94 
95 	assert(calls == 4);
96 }
97 
98 Promise!(T[]) readAll(T)(PromiseIterator!T input) nothrow {
99 	T[] r;
100 	return input.each((value) {
101 		r ~= value;
102 	}).then((_) => r);
103 }
104 unittest {
105 	int[] all;
106 	[1, 2, 3].toAsync.readAll.then((allArg) {
107 		all = allArg;
108 	}).nothrow_();
109 
110 	assert(all == [1, 2, 3]);
111 }
112 
113 Promise!(T[]) readAllChunks(T)(PromiseIterator!(T[]) input) nothrow {
114 	T[] r;
115 	return input.each((value) {
116 		r ~= value;
117 	}).then((_) => r);
118 }
119 unittest {
120 	const(char)[] all;
121 	["Hello ", "World"].toAsync.readAllChunks.then((allArg) {
122 		all = allArg;
123 	}).nothrow_();
124 
125 	assert(all == "Hello World");
126 }