1 module game.chain; 2 3 import std.meta : allSatisfy, staticMap; 4 import std.traits : CommonType, isCallable, isFloatingPoint, isIntegral, 5 isPointer, isSomeFunction, isStaticArray, Unqual, isInstanceOf; 6 import std.range.primitives; 7 8 auto chain(Ranges...)(Ranges rs) 9 if (Ranges.length > 0 && 10 allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) && 11 !is(CommonType!(staticMap!(ElementType, staticMap!(Unqual, Ranges))) == void)) 12 { 13 static if (Ranges.length == 1) 14 { 15 return rs[0]; 16 } 17 else 18 { 19 static struct Result 20 { 21 private: 22 alias R = staticMap!(Unqual, Ranges); 23 alias RvalueElementType = CommonType!(staticMap!(.ElementType, R)); 24 private template sameET(A) 25 { 26 enum sameET = is(.ElementType!A == RvalueElementType); 27 } 28 29 enum bool allSameType = allSatisfy!(sameET, R); 30 31 // This doesn't work yet 32 static if (allSameType) 33 { 34 alias ElementType = ref RvalueElementType; 35 } 36 else 37 { 38 alias ElementType = RvalueElementType; 39 } 40 static if (allSameType && allSatisfy!(hasLvalueElements, R)) 41 { 42 static ref RvalueElementType fixRef(ref RvalueElementType val) 43 { 44 return val; 45 } 46 } 47 else 48 { 49 static RvalueElementType fixRef(RvalueElementType val) 50 { 51 return val; 52 } 53 } 54 55 // This is the entire state 56 R source; 57 // TODO: use a vtable (or more) instead of linear iteration 58 59 public: 60 this(R input) 61 { 62 foreach (i, v; input) 63 { 64 source[i] = v; 65 } 66 } 67 68 import std.meta : anySatisfy; 69 70 static if (anySatisfy!(isInfinite, R)) 71 { 72 // Propagate infiniteness. 73 enum bool empty = false; 74 } 75 else 76 { 77 @property bool empty() 78 { 79 foreach (i, Unused; R) 80 { 81 if (!source[i].empty) return false; 82 } 83 return true; 84 } 85 } 86 87 void popFront() 88 { 89 foreach (i, Unused; R) 90 { 91 if (source[i].empty) continue; 92 source[i].popFront(); 93 return; 94 } 95 } 96 97 @property auto ref front() 98 { 99 foreach (i, Unused; R) 100 { 101 if (source[i].empty) continue; 102 return fixRef(source[i].front); 103 } 104 assert(false); 105 } 106 107 static if (allSameType && allSatisfy!(hasAssignableElements, R)) 108 { 109 // @@@BUG@@@ 110 //@property void front(T)(T v) if (is(T : RvalueElementType)) 111 112 @property void front(RvalueElementType v) 113 { 114 foreach (i, Unused; R) 115 { 116 if (source[i].empty) continue; 117 source[i].front = v; 118 return; 119 } 120 assert(false); 121 } 122 } 123 124 static if (allSatisfy!(hasMobileElements, R)) 125 { 126 RvalueElementType moveFront() 127 { 128 foreach (i, Unused; R) 129 { 130 if (source[i].empty) continue; 131 return source[i].moveFront(); 132 } 133 assert(false); 134 } 135 } 136 137 static if (allSatisfy!(isBidirectionalRange, R)) 138 { 139 @property auto ref back() 140 { 141 foreach_reverse (i, Unused; R) 142 { 143 if (source[i].empty) continue; 144 return fixRef(source[i].back); 145 } 146 assert(false); 147 } 148 149 void popBack() 150 { 151 foreach_reverse (i, Unused; R) 152 { 153 if (source[i].empty) continue; 154 source[i].popBack(); 155 return; 156 } 157 } 158 159 static if (allSatisfy!(hasMobileElements, R)) 160 { 161 RvalueElementType moveBack() 162 { 163 foreach_reverse (i, Unused; R) 164 { 165 if (source[i].empty) continue; 166 return source[i].moveBack(); 167 } 168 assert(false); 169 } 170 } 171 172 static if (allSameType && allSatisfy!(hasAssignableElements, R)) 173 { 174 @property void back(RvalueElementType v) 175 { 176 foreach_reverse (i, Unused; R) 177 { 178 if (source[i].empty) continue; 179 source[i].back = v; 180 return; 181 } 182 assert(false); 183 } 184 } 185 } 186 187 static if (allSatisfy!(hasLength, R)) 188 { 189 @property size_t length() 190 { 191 size_t result; 192 foreach (i, Unused; R) 193 { 194 result += source[i].length; 195 } 196 return result; 197 } 198 199 alias opDollar = length; 200 } 201 202 static if (allSatisfy!(isRandomAccessRange, R)) 203 { 204 auto ref opIndex(size_t index) 205 { 206 foreach (i, Range; R) 207 { 208 static if (isInfinite!(Range)) 209 { 210 return source[i][index]; 211 } 212 else 213 { 214 immutable length = source[i].length; 215 if (index < length) return fixRef(source[i][index]); 216 index -= length; 217 } 218 } 219 assert(false); 220 } 221 222 static if (allSatisfy!(hasMobileElements, R)) 223 { 224 RvalueElementType moveAt(size_t index) 225 { 226 foreach (i, Range; R) 227 { 228 static if (isInfinite!(Range)) 229 { 230 return source[i].moveAt(index); 231 } 232 else 233 { 234 immutable length = source[i].length; 235 if (index < length) return source[i].moveAt(index); 236 index -= length; 237 } 238 } 239 assert(false); 240 } 241 } 242 243 static if (allSameType && allSatisfy!(hasAssignableElements, R)) 244 void opIndexAssign(ElementType v, size_t index) 245 { 246 foreach (i, Range; R) 247 { 248 static if (isInfinite!(Range)) 249 { 250 source[i][index] = v; 251 } 252 else 253 { 254 immutable length = source[i].length; 255 if (index < length) 256 { 257 source[i][index] = v; 258 return; 259 } 260 index -= length; 261 } 262 } 263 assert(false); 264 } 265 } 266 267 static if (allSatisfy!(hasLength, R) && allSatisfy!(hasSlicing, R)) 268 auto opSlice(size_t begin, size_t end) return scope 269 { 270 auto result = this; 271 foreach (i, Unused; R) 272 { 273 immutable len = result.source[i].length; 274 if (len < begin) 275 { 276 result.source[i] = result.source[i] 277 [len .. len]; 278 begin -= len; 279 } 280 else 281 { 282 result.source[i] = result.source[i] 283 [begin .. len]; 284 break; 285 } 286 } 287 auto cut = length; 288 cut = cut <= end ? 0 : cut - end; 289 foreach_reverse (i, Unused; R) 290 { 291 immutable len = result.source[i].length; 292 if (cut > len) 293 { 294 result.source[i] = result.source[i] 295 [0 .. 0]; 296 cut -= len; 297 } 298 else 299 { 300 result.source[i] = result.source[i] 301 [0 .. len - cut]; 302 break; 303 } 304 } 305 return result; 306 } 307 } 308 return Result(rs); 309 } 310 }