1 module nxt.roptional; 2 3 import nxt.nullable_traits : isNullable; 4 5 private struct None {} 6 7 struct Optional(T) 8 { 9 import core.internal.traits : Unqual; 10 private Unqual!T value; 11 12 static if (!isNullable!T) 13 { 14 private bool present; 15 } 16 17 this(T value) 18 { 19 opAssign(value); 20 } 21 22 this(None) 23 { 24 } 25 26 void opAssign(T value) 27 { 28 this.value = value; 29 static if (!isNullable!T) 30 { 31 present = true; 32 } 33 } 34 35 void opAssign(None) 36 { 37 static if (isNullable!T) 38 { 39 value = null; 40 } 41 else 42 { 43 present = false; 44 } 45 } 46 47 bool isPresent() const 48 { 49 static if (isNullable!T) 50 { 51 return value !is null; 52 } 53 else 54 { 55 return present; 56 } 57 } 58 59 T get() 60 { 61 assert(isPresent); 62 return value; 63 } 64 65 T or(lazy T alternativeValue) 66 { 67 return isPresent ? value : alternativeValue; 68 } 69 70 bool empty() const 71 { 72 return !isPresent; 73 } 74 75 T front() 76 { 77 return get; 78 } 79 80 void popFront() 81 { 82 static if (isNullable!T) 83 { 84 value = null; 85 } 86 else 87 { 88 present = false; 89 } 90 } 91 92 size_t length() const 93 { 94 return isPresent ? 1 : 0; 95 } 96 97 // auto ref opDispatch(string name, Args...)(auto ref Args args) 98 // { 99 // import std.traits : PointerTarget, isPointer; 100 // import dlp.core.traits : hasField, TypeOfMember, getMember; 101 102 // static if (isPointer!T) 103 // alias StoredType = PointerTarget!T; 104 // else 105 // alias StoredType = T; 106 107 // static if (is(StoredType == class) || is(StoredType == struct)) 108 // { 109 // static if (hasField!(StoredType, name)) 110 // { 111 // alias FieldType = TypeOfMember!(StoredType, name); 112 113 // if (isPresent) 114 // return optional(value.getMember!name); 115 // else 116 // return none!FieldType; 117 // } 118 // else 119 // { 120 // alias ReturnType = typeof(__traits(getMember, value, name)(args)); 121 122 // if (isPresent) 123 // return optional(__traits(getMember, value, name)(args)); 124 // else 125 // return none!ReturnType; 126 // } 127 // } 128 // else 129 // { 130 // return optional(value.getMember!name); 131 // } 132 133 // assert(0); 134 // } 135 136 // @safe pure nothrow @nogc unittest 137 // { 138 // assert(Optional!Foo(Foo(3)).a.get == 3); 139 // assert(Optional!Foo.init.a.empty); 140 141 // assert(Optional!Foo(Foo()).opDispatch!"c"(4).get == 4); 142 // assert(Optional!Foo.init.c(4).empty); 143 144 // assert(Optional!Foo(Foo(1, new Bar(5))).b.a.get == 5); 145 // assert(Optional!Foo(Foo(1)).b.a.empty); 146 // } 147 } 148 149 @safe pure nothrow @nogc unittest 150 { 151 enum newVale = 4; 152 Optional!int a = 3; 153 a = newVale; 154 assert(a.get == newVale); 155 } 156 157 @safe pure nothrow @nogc unittest 158 { 159 Optional!int a = 3; 160 a = none; 161 assert(!a.isPresent); 162 } 163 164 @safe pure nothrow @nogc unittest 165 { 166 Optional!int a = 3; 167 assert(a.isPresent); 168 169 Optional!(int*) b = null; 170 assert(!b.isPresent); 171 } 172 173 @safe pure nothrow @nogc unittest 174 { 175 Optional!int a = 3; 176 assert(a.get == 3); 177 } 178 179 @safe pure /*nothrow*/ unittest 180 { 181 Optional!int a = 3; 182 assert(a.or(4) == 3); 183 184 Optional!int b = none; 185 assert(b.or(4) == 4); 186 } 187 188 @safe pure nothrow @nogc unittest 189 { 190 Optional!int a = 3; 191 assert(!a.empty); 192 193 Optional!int b = none; 194 assert(b.empty); 195 } 196 197 @safe pure nothrow @nogc unittest 198 { 199 Optional!int a = 3; 200 assert(a.get == 3); 201 } 202 203 /** Instantiate an `Optional` `value`. */ 204 Optional!T optional(T)(T value) 205 { 206 return Optional!T(value); 207 } 208 209 /// 210 @safe pure nothrow @nogc unittest 211 { 212 Optional!int a = 3; 213 a.popFront(); 214 assert(!a.isPresent); 215 } 216 217 /// 218 @safe pure nothrow @nogc unittest 219 { 220 Optional!int a = 3; 221 assert(a.length == 1); 222 223 Optional!int b = none; 224 assert(b.length == 0); 225 } 226 227 /// 228 @safe pure nothrow @nogc unittest 229 { 230 assert(optional(3).isPresent); 231 } 232 233 /// 234 @trusted pure nothrow @nogc unittest 235 { 236 int i; 237 assert(optional(&i).isPresent); 238 assert(!optional!(int*)(null).isPresent); 239 } 240 241 /// 242 @safe pure nothrow @nogc unittest 243 { 244 import std.algorithm : map; 245 enum value = 3; 246 assert(optional(value).map!(e => e).front == value); 247 } 248 249 Optional!T some(T)(T value) 250 in 251 { 252 static if (isNullable!T) 253 { 254 assert(value !is null); 255 } 256 } 257 do 258 { 259 Optional!T o; 260 o.value = value; 261 o.present = true; 262 263 return o; 264 } 265 266 /// 267 @safe pure nothrow @nogc unittest 268 { 269 assert(some(3).isPresent); 270 } 271 272 @safe pure nothrow @nogc None none() 273 { 274 return None(); 275 } 276 277 Optional!T none(T)() 278 { 279 return Optional!T.init; 280 } 281 282 /// 283 @safe pure nothrow @nogc unittest 284 { 285 assert(!none!int.isPresent); 286 } 287 288 version (unittest) 289 { 290 // Cannot put this inside the @safe pure nothrow @nogc unittest block due to 291 // https://issues.dlang.org/show_bug.cgi?id=19157 292 private struct Foo 293 { 294 int a; 295 Bar* b; 296 297 int c(int a) 298 { 299 return a; 300 } 301 302 Bar d(int a) 303 { 304 return Bar(a); 305 } 306 } 307 308 private struct Bar 309 { 310 int a; 311 312 int foo(int a) 313 { 314 return a; 315 } 316 } 317 }