1 /**
2  * ae.utils.meta.chain
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 <vladimir@thecybershadow.net>
12  */
13 
14 /// Chains are a concept a bit like ranges,
15 /// but which operate on heterogenous types
16 /// (e.g. a tuple of values).
17 
18 /// Composition is done by a chain of functors.
19 /// To allow state, each functor can be represented
20 /// as a struct.
21 
22 /// Functors return a bool (true if iteration should
23 /// stop, false if it should continue).
24 
25 module ae.utils.meta.chain;
26 
27 import ae.utils.meta.caps;
28 
29 ///
30 static if (haveAliasStructBinding)
31 unittest
32 {
33 	int a = 2;
34 	int x;
35 	chainIterator(chainFilter!(n => n > a)((int n) { x = n; return true; } ))(1, 2, 3);
36 	assert(x == 3);
37 }
38 
39 static if (haveAliasStructBinding)
40 unittest
41 {
42 	static struct X
43 	{
44 		bool fun(int x)
45 		{
46 			return chainIterator(chainFunctor!(n => n == x))(this.tupleof);
47 		}
48 
49 		long a;
50 		int b;
51 		ubyte c;
52 	}
53 
54 	X x;
55 	assert(!x.fun(5));
56 	x.c = 5;
57 	assert(x.fun(5));
58 }
59 
60 /// Starts the chain by iterating over a tuple.
61 struct ChainIterator(Next)
62 {
63 	Next next;
64 
65 	this(ref Next next)
66 	{
67 		this.next = next;
68 	}
69 
70 	bool opCall(Args...)(auto ref Args args)
71 	{
72 		foreach (ref arg; args)
73 			if (next(arg))
74 				return true;
75 		return false;
76 	}
77 }
78 auto chainIterator(Next)(Next next)
79 {
80 	return ChainIterator!Next(next);
81 }
82 
83 unittest
84 {
85 	struct S
86 	{
87 		int a = 1;
88 		long b = 2;
89 		ubyte c = 3;
90 	}
91 	S s;
92 
93 	int[] results;
94 	chainIterator((long n) { results ~= cast(int)n; return  false; })(s.tupleof);
95 	assert(results == [1, 2, 3]);
96 }
97 
98 /// Wraps a function template into a concrete value type functor.
99 struct ChainFunctor(alias fun)
100 {
101 	auto opCall(Arg)(auto ref Arg arg)
102 	{
103 		return fun(arg);
104 	}
105 }
106 auto chainFunctor(alias fun)() /// ditto
107 {
108 	ChainFunctor!fun s;
109 	return s;
110 }
111 
112 ///
113 static if (haveAliasStructBinding)
114 unittest
115 {
116 	int[] results;
117 	auto fn = chainFunctor!(n => results ~= cast(int)n);
118 	fn(1);
119 	fn(long(2));
120 	fn(ubyte(3));
121 	assert(results == [1, 2, 3]);
122 }
123 
124 /// Calls next only if pred(value) is true.
125 struct ChainFilter(alias pred, Next)
126 {
127 	Next next;
128 
129 	this(Next next) { this.next = next; }
130 
131 	bool opCall(T)(auto ref T v)
132 	{
133 		if (pred(v))
134 			return next(v);
135 		return false;
136 	}
137 }
138 template chainFilter(alias pred) /// ditto
139 {
140 	auto chainFilter(Next)(Next next)
141 	{
142 		return ChainFilter!(pred, Next)(next);
143 	}
144 }
145 
146 struct ChainControl(bool result, Next)
147 {
148 	Next next;
149 
150 	this(Next next) { this.next = next; }
151 
152 	bool opCall(T)(auto ref T v)
153 	{
154 		cast(void)next(v);
155 		return result;
156 	}
157 }
158 template chainControl(bool result) /// ditto
159 {
160 	auto chainControl(Next)(Next next)
161 	{
162 		return ChainControl!(result, Next)(next);
163 	}
164 }
165 alias chainAll = chainControl!false; // Always continue iteration
166 alias chainFirst = chainControl!true; // Stop iteration after this element
167 
168 ///
169 static if (haveAliasStructBinding)
170 unittest
171 {
172 	int a = 2;
173 	int b = 3;
174 	int[] results;
175 	foreach (i; 0..10)
176 		chainFilter!(n => n % a == 0)(chainFilter!(n => n % b == 0)((int n) { results ~= n; return false; }))(i);
177 	assert(results == [0, 6]);
178 }
179 
180 /// Calls next with pred(value).
181 struct ChainMap(alias pred, Next)
182 {
183 	Next next;
184 
185 	this(Next next) { this.next = next; }
186 
187 	bool opCall(T)(auto ref T v)
188 	{
189 		return next(pred(v));
190 	}
191 }
192 template chainMap(alias pred) /// ditto
193 {
194 	auto chainMap(Next)(Next next)
195 	{
196 		return ChainMap!(pred, Next)(next);
197 	}
198 }
199 
200 ///
201 unittest
202 {
203 	int result;
204 	chainMap!(n => n+1)(chainMap!(n => n * 2)((int n) { result = n; return false; }))(2);
205 	assert(result == 6);
206 }