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