1 module aermicioi.aedi.util.range; 2 3 import std.range.primitives; 4 5 /** 6 Buffering output range into a static array 7 8 Parameters: 9 T = static array buffer. 10 **/ 11 struct BufferSink(T : Z[N], Z, size_t N) { 12 private T buffer; 13 private size_t fillage; 14 15 /** 16 Pull elements out of range and save them in buffer 17 18 Params: 19 range = range from which to pull out elements. 20 **/ 21 void put(R)(ref R range) 22 if (isInputRange!R && is(ElementType!R == Z)){ 23 24 if (range.empty) { 25 return; 26 } 27 28 foreach (index, ref slot; buffer) { 29 if (range.empty) { 30 break; 31 } 32 33 slot = range.front; 34 range.popFront; 35 fillage = index; 36 } 37 38 ++fillage; 39 } 40 41 /** 42 Get buffered data. 43 44 Returns: 45 Z[] a slice of static array 46 **/ 47 Z[] slice() { 48 return buffer[0 .. fillage]; 49 } 50 } 51 52 /** 53 A forward range representing an exception chain. 54 55 Params: 56 throwable = starting point of exception chain 57 58 Returns: 59 a forward range of exceptions 60 **/ 61 auto exceptions(Throwable throwable) { 62 return ExceptionChain(throwable); 63 } 64 65 /** 66 ditto 67 **/ 68 @safe struct ExceptionChain { 69 70 private Throwable current; 71 72 public { 73 /** 74 Constructor for exception chain 75 76 Params: 77 exception = initial exception in chain of exceptions 78 **/ 79 this(Throwable exception) { 80 this.current = exception; 81 } 82 83 private this(ref ExceptionChain chain) { 84 this.current = chain.current; 85 } 86 87 /** 88 Get current throwable in exception chain 89 90 Returns: 91 current throwable 92 **/ 93 Throwable front() { 94 return current; 95 } 96 97 /** 98 Check if exception chain exhausted or not. 99 100 Returns: 101 true if exausted false otherwise 102 **/ 103 bool empty() { 104 return current is null; 105 } 106 107 /** 108 Move to next exception in chain 109 **/ 110 void popFront() { 111 if (!empty) { 112 current = current.next; 113 } 114 } 115 116 /** 117 Save exception chain at current point. 118 119 Returns: 120 A copy of current exception chain 121 **/ 122 typeof(this) save() { 123 return ExceptionChain(this); 124 } 125 } 126 } 127 128 /** 129 Given a range of objects filter them by Interface they are implementing. 130 131 Params: 132 Interface = interface by which to filter the range 133 range = range of objects to filter 134 135 Returns: 136 InterfaceFilter!(Range, Interface) a range of filtered objects by Interface 137 **/ 138 InterfaceFilter!(Range, Interface) filterByInterface(Interface, Range)(auto ref Range range) { 139 return InterfaceFilter!(Range, Interface)(range); 140 } 141 142 /** 143 ditto 144 **/ 145 @safe struct InterfaceFilter(Range, Interface) 146 if (isForwardRange!Range && (is(ElementType!Range == class) || is(ElementType!Range == interface))) { 147 148 private Range range; 149 private Interface current; 150 151 this(this) { 152 range = range.save; 153 } 154 155 /** 156 Constructor for interface filtering range 157 158 Params: 159 range = range to filter out. 160 **/ 161 this(ref Range range) { 162 this.range = range.save; 163 this.popFront; 164 } 165 166 private this(ref Range range, Interface current) { 167 this.range = range.save; 168 this.current = current; 169 } 170 171 /** 172 Whether there are more elements that implement interface or not. 173 174 Returns: 175 true if there are no more elements implementing interface 176 **/ 177 bool empty() { 178 return current is null; 179 } 180 181 /** 182 Get current implementing element 183 184 Returns: 185 current element that implements interface 186 **/ 187 Interface front() { 188 return current; 189 } 190 191 /** 192 Move to next element implementing interface. 193 **/ 194 void popFront() @trusted { 195 while (!range.empty) { 196 auto front = range.front; 197 range.popFront; 198 199 current = cast(Interface) front; 200 201 if (current !is null) { 202 return; 203 } 204 } 205 206 current = null; 207 } 208 209 /** 210 Save range at current point. 211 212 Returns: 213 A copy of current range. 214 **/ 215 typeof(this) save() { 216 return typeof(this)(range, current); 217 } 218 } 219 220 /** 221 Create a range of all base classes and interfaces a component implements. 222 223 Warning no order of inherited interfaces and classes is guaranteed. 224 225 Params: 226 classinfo = typeinfo of component for which to create inheritance chain. 227 228 Returns: 229 A range of ClassInfo elements. 230 **/ 231 auto inheritance(ClassInfo classinfo) { 232 return InheritanceRange(classinfo); 233 } 234 235 /** 236 ditto 237 **/ 238 @safe struct InheritanceRange { 239 private { 240 ClassInfo[] stack; 241 ClassInfo[] processed; 242 } 243 244 /** 245 Constructor for inheritance range 246 247 Params: 248 component = component for which to create inheritance range. 249 **/ 250 this(ClassInfo component) { 251 stack = [ component ]; 252 } 253 254 /** 255 Copy constructor for InheritanceRange, ala save. 256 257 Params: 258 range = copied range. 259 **/ 260 this(ref InheritanceRange range) { 261 this.stack = range.stack[]; 262 this.processed = range.processed[]; 263 } 264 265 /** 266 Get current element in range. 267 268 Returns: 269 Current type that component inherits. 270 **/ 271 ClassInfo front() { 272 return stack.front; 273 } 274 275 /** 276 Whether there are more inherited types or not. 277 278 Returns: 279 true if not. 280 **/ 281 bool empty() { 282 return stack.empty; 283 } 284 285 /** 286 Move to next inherited type. 287 **/ 288 void popFront() { 289 import std.algorithm.iteration : map, filter; 290 import std.algorithm.searching : canFind; 291 import std.range : chain, only; 292 ClassInfo removed = stack.front; 293 stack.popFront; 294 processed ~= removed; 295 296 foreach ( 297 unprocessed; 298 removed.interfaces 299 .map!(iface => iface.classinfo) 300 .chain(removed.base.only) 301 .filter!(iface => iface !is null) 302 .filter!(iface => !processed.canFind!((f, s) => f is s)(iface)) 303 ) { 304 stack ~= unprocessed; 305 } 306 } 307 308 /** 309 Save range at current state. 310 311 Returns: 312 A copy of range. 313 **/ 314 InheritanceRange save() { 315 return InheritanceRange(this); 316 } 317 }