1 module aermicioi.aedi.util.typecons; 2 3 import std..string : stripRight; 4 5 @safe: 6 7 /** 8 Creates a lightweight tuple of values. 9 10 Params: 11 args = list of arguments to pack in a tuple. 12 Returns: 13 Tuple!Args a tuple of arguments, probably auto expandable thx to alias this. 14 **/ 15 auto tuple(Args...)(auto ref Args args) { 16 return Tuple!Args(args); 17 } 18 19 /** 20 ditto 21 **/ 22 struct Tuple(Args...) { 23 24 /** 25 The packed arguments. 26 **/ 27 Args args; 28 29 /** 30 Alias this to args, for possible auto expansion. 31 **/ 32 alias args this; 33 34 /** 35 Compatibility alias to expand functionality of std.typecons.Tuple 36 **/ 37 alias expand = args; 38 } 39 40 /** 41 Pseudo immutable optional with a value or not. 42 43 Params: 44 optional = value to contain in the optional itself. 45 46 Returns: 47 An optional filled or not with value. 48 **/ 49 Optional!T optional(T)(return ref T optional) { 50 return Optional!T(optional); 51 } 52 53 /** 54 ditto 55 **/ 56 Optional!T optional(T)(return T optional) { 57 return Optional!T(optional); 58 } 59 60 /** 61 ditto 62 **/ 63 Optional!T optional(T)() { 64 return Optional!T(); 65 } 66 67 /** 68 ditto 69 **/ 70 struct Optional(T) { 71 private T payload_; 72 immutable bool isNull = true; 73 74 /** 75 Constructor for pseudo immutable optional. 76 77 Params: 78 payload = payload to hold onto. 79 **/ 80 this(ref T payload) return { 81 static if (is(T : Z*, Z) || is(T == class) || is(T == interface)) { 82 this.isNull = payload is null; 83 } else { 84 this.isNull = false; 85 } 86 87 this.payload_ = payload; 88 } 89 90 /** 91 Return a const version of this optional. 92 93 Returns: 94 Optional!(const T) a constant version. 95 **/ 96 Optional!(const T) opCast(X : const T)() { 97 return Optional!(const T)(payload); 98 } 99 100 /** 101 Get the payload hosted in this optional. 102 103 Returns: 104 T payload that is hosted. 105 **/ 106 ref inout(T) payload() inout pure nothrow @nogc @safe 107 out (; !isNull, "Cannot provide a value out of optional when it is null.") { 108 return this.payload_; 109 } 110 111 /** 112 Get the payload if exists or provide an alternative. 113 114 Params: 115 alternative = alternative to supply 116 117 Returns: 118 Always a payload to use. 119 **/ 120 ref inout(T) orElse(return ref scope inout(T) alternative) inout pure nothrow @nogc @safe { 121 122 if (this == null) { 123 return alternative; 124 } 125 126 return this.payload_; 127 } 128 129 void orElseThrow(T : Exception)(lazy T exception) inout pure @safe { 130 if (this == null) { 131 throw exception; 132 } 133 } 134 135 /** 136 Check whether optional is null or not. 137 138 Params: 139 term = null term to accept. 140 141 Returns: 142 true if null, false otherwise 143 **/ 144 bool opEquals(inout typeof(null) term) inout pure nothrow @nogc @safe { 145 return this.isNull; 146 } 147 148 bool opEquals()(inout lazy T value) inout { 149 if (this == null) { 150 return false; 151 } 152 153 return this.payload == value; 154 } 155 156 /** 157 Convenience subtyping. 158 **/ 159 alias payload this; 160 } 161 162 /** 163 Pair a key and a value toghether. 164 165 Params: 166 value = value to pair with key 167 key = key associated to value 168 169 Returns: 170 Pair!(ValueType, KeyType) a pair of key => value. 171 **/ 172 Pair!(ValueType, KeyType) pair(ValueType, KeyType)(auto ref ValueType value, auto ref KeyType key) { 173 return Pair!(ValueType, KeyType)(value, key); 174 } 175 176 /** 177 ditto 178 **/ 179 alias paired = pair; 180 181 /** 182 ditto 183 **/ 184 struct Pair(ValueType, KeyType) { 185 186 /** 187 Value in pair 188 **/ 189 ValueType value; 190 191 /** 192 Key in pair 193 **/ 194 KeyType key; 195 } 196 197 /** 198 Interface for objects that can be subscribed to specific events emmited by them. 199 200 Params: 201 EventType = type of events for which implementor will call subscribers. 202 Args = list of arguments accepted by subscriber 203 **/ 204 @safe interface Subscribable(EventType, Callback) 205 { 206 207 public 208 { 209 210 /** 211 Subscribe a delegate to a particular event emmited by object 212 213 Params: 214 type = type of event emmited by object 215 subscriber = the callback to be called on event emmited 216 Returns: 217 typeof(this) 218 **/ 219 Subscribable subscribe(EventType type, Callback subscriber); 220 } 221 } 222 223 /** 224 A default mixin implementing storage logic for subscribers. 225 **/ 226 mixin template SubscribableMixin(EventType, Callback) { 227 private { 228 Callback[][EventType] subscribers; 229 } 230 231 @safe public 232 { 233 234 /** 235 Subscribe a delegate to a particular event emmited by object 236 237 Params: 238 type = type of event emmited by object 239 subscriber = the callback to be called on event emmited 240 Returns: 241 typeof(this) 242 **/ 243 typeof(this) subscribe(EventType type, Callback subscriber) 244 in (subscriber !is null, "Cannot subcribe to event, when no subscriber is passed(null).") 245 { 246 subscribers[type] ~= subscriber; 247 248 return this; 249 } 250 } 251 252 @safe private { 253 /** 254 Share subscribers with another subscribable. 255 256 Params: 257 subscribable = subscribable that wil receive subscribers 258 259 Returns: 260 this 261 **/ 262 inout(typeof(this)) transfer(Subscribable!(EventType, Callback) subscribable) inout { 263 foreach (key, subscribers; subscribers) { 264 foreach (subscriber; subscribers) { 265 subscribable.subscribe(key, subscriber); 266 } 267 } 268 269 return this; 270 } 271 272 /** 273 Get subscribers for particular event. 274 275 Params: 276 type = event type 277 278 Returns: 279 List of subscribers 280 **/ 281 inout(Callback)[] subscribersOfEvent(EventType type) inout { 282 if (type in subscribers) { 283 return subscribers[type]; 284 } 285 286 return []; 287 } 288 289 private alias Params = Parameters!Callback; 290 291 /** 292 Invokes all subscribers for an event with arguments. 293 294 Params: 295 type = event type 296 args = list of arguments to subscriber 297 **/ 298 ReturnType!Callback invoke(EventType type, Params params) const { 299 import std.range : empty; 300 static if (!is(ReturnType!Callback == void)) { 301 ReturnType result; 302 } 303 304 auto subscribers = subscribersOfEvent(type); 305 306 if (subscribers.empty) { 307 static if (!is(ReturnType!Callback == void)) { 308 return result; 309 } else { 310 return; 311 } 312 } 313 314 foreach (subscriber; subscribers[0 .. $ - 1]) { 315 subscriber(params); 316 } 317 318 static if (!is(ReturnType!Callback == void)) { 319 return subscribers[$ - 1](params); 320 } else { 321 subscribers[$ - 1](params); 322 } 323 } 324 325 static if (!is(ReturnType!Callback == void) && (Params.length > 0)) { 326 /** 327 Invokes all subscribers for an event with arguments. 328 329 Compared to invoke method, this method will pass return value of 330 subscribers to subsequent invoked ones, if possible. 331 332 Params: 333 at = at which argument position to replace argument from args with return value of precedent value; 334 type = event type 335 args = list of arguments to subscriber 336 **/ 337 ReturnType transform(size_t at)(EventType type, Params args) const 338 if ((at < args.length) && is(ReturnType : Args[at])) { 339 ReturnType result = args[at]; 340 341 foreach (subscriber; subscribersOfEvent(type)) { 342 result = subscriber(args[0 .. at], result, args[at + 1 .. $]); 343 } 344 345 static if (!is(ReturnType == void)) { 346 return result; 347 } 348 } 349 } 350 } 351 } 352 353 /** 354 Mix in an all args constructor 355 **/ 356 mixin template AllArgsConstructor() { 357 358 /** 359 Generated constructor accepting all defined fields. 360 361 Params: 362 args = list of fields as arguments to constructor 363 364 Returns: 365 Fully constructed typeof(this) instance 366 **/ 367 this()(auto ref inout typeof(this.tupleof) args) inout { 368 369 this.tupleof = args; 370 } 371 } 372 373 mixin template ArgsConstructor(Args...) { 374 375 debug pragma(msg, typeof(Args)); 376 377 /** 378 Generated constructor accepting a list of defined fields. 379 380 Params: 381 args = list of fields as arguments to constructor 382 383 Returns: 384 Fully constructed typeof(this) instance 385 **/ 386 this()(auto ref inout scope typeof(Args) args) { 387 static foreach (index, arg; Args) { 388 arg = args[index]; 389 } 390 } 391 } 392 393 /** 394 Mix in a copy constructor 395 **/ 396 mixin template CopyConstructor() { 397 398 /** 399 Generated copy constructor. 400 401 Params: 402 copyable = copyable instance 403 404 Returns: 405 Fully constructed typeof(this) instance 406 **/ 407 this()(auto ref scope typeof(this) copyable) { 408 this.tupleof = copyable.tupleof; 409 } 410 }