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