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