1 module mage.util.option; 2 import mage; 3 4 struct Option(T) { 5 alias WrappedType = T; 6 alias WrapperType = VariantN!(T.sizeof, T); 7 8 /// The underlying value wrapped in a `std.VariantN`. 9 WrapperType value; 10 11 this(U)(auto ref U u) { 12 this.value = cast(WrappedType)u; 13 } 14 15 /// Whether this option has some value. 16 bool isSome() inout { return this.value.hasValue(); } 17 18 /// Whether this option does not have some value. 19 bool isNone() inout { return !this.isSome(); } 20 21 /// Clear the option so it no longer has a value. 22 void clear() { this.value = WrapperType(); } 23 24 /// Implicit conversion to bool. Equivalent to isSome(). 25 bool opCast(CastTargetType : bool)() inout { return this.isSome(); } 26 27 /// Return the wrapped value. Throws an exception if there is no value. 28 auto ref inout(WrappedType) unwrap(string msg = null) inout { 29 return *this.value.peek!(WrappedType).enforce(msg ? msg : "This option has nothing to unwrap."); 30 } 31 32 void opAssign(U : WrappedType)(auto ref U u) { 33 this.value = cast(WrappedType)u; 34 } 35 } 36 37 38 unittest 39 { 40 import std.exception; 41 42 Option!int opt; 43 assert(opt.isNone); 44 assert(!opt.isSome); 45 assert(!opt); 46 assertThrown(opt.unwrap()); 47 opt = 42; 48 assert(!opt.isNone); 49 assert(opt.isSome); 50 assert(opt); 51 assert(opt.unwrap() == 42); 52 opt.clear(); 53 assert(opt.isNone); 54 } 55 56 string toString(T)(in ref Option!T opt) { 57 if(opt.isSome) { 58 return "Option!%s(%s)".format(T.stringof, opt.unwrap()); 59 } 60 return "Option!%s()".format(T.stringof); 61 } 62 63 unittest 64 { 65 Option!int opt; 66 assert(opt.toString() == "Option!int()"); 67 opt = 42; 68 assert(opt.toString() == "Option!int(42)"); 69 }