1 module gfx.core.typecons; 2 3 /// A transition from one state to another 4 struct Trans(T) { 5 /// state before 6 T from; 7 /// state after 8 T to; 9 } 10 11 /// Transition build helper 12 auto trans(T)(T from, T to) { 13 return Trans!T(from, to); 14 } 15 16 /// template that resolves to true if an object of type T can be assigned to null 17 template isNullAssignable(T) { 18 enum isNullAssignable = 19 is(typeof((inout int = 0) { 20 T t = T.init; 21 t = null; 22 })); 23 } 24 25 version(unittest) { 26 private interface ITest {} 27 private class CTest {} 28 private struct STest {} 29 static assert( isNullAssignable!ITest); 30 static assert( isNullAssignable!CTest); 31 static assert(!isNullAssignable!STest); 32 static assert( isNullAssignable!(STest*)); 33 } 34 35 /// constructs an option from a value 36 auto some(T)(T val) { 37 static if (isNullAssignable!T) { 38 return Option!T(val); 39 } 40 else { 41 import std.traits : Unqual; 42 return Option!(Unqual!T)(val); 43 } 44 } 45 46 /// symbolic value that constructs an Option in none state 47 enum none(T) = Option!(T).init; 48 49 /// Check that init value yields a none 50 unittest { 51 52 auto vopt = none!int; 53 assert(vopt.isNone); 54 vopt = 12; 55 assert(vopt.isSome); 56 assert(vopt.front == 12); 57 58 // auto ropt = none!CTest; 59 // assert(ropt.isNone); 60 // assert(ropt._val is null); 61 // ropt = new CTest; 62 // assert(vopt.isSome); 63 } 64 65 auto option(R)(R input) if (isInputRange!R) 66 { 67 alias T = ElementType!R; 68 Option!T res; 69 70 if (!input.empty) { 71 res = input.front; 72 input.popFront(); 73 assert(input.empty, "attempt to build Option with more than one element)"); 74 } 75 76 return res; 77 } 78 79 80 struct Option(T) 81 { 82 import std.traits : Unqual; 83 alias ValT = Unqual!T; 84 private ValT _val = ValT.init; 85 86 static if (isNullAssignable!ValT) { 87 this(inout ValT val) inout { 88 _val = val; 89 } 90 91 @property bool isSome() const { 92 return _val !is null; 93 } 94 95 @property bool isNone() const { 96 return _val is null; 97 } 98 99 void setNone() { 100 _val = null; 101 } 102 103 void opAssign()(T val) { 104 _val = val; 105 } 106 } 107 else { 108 private bool _isSome = false; 109 110 this(inout ValT val) inout { 111 _val = val; 112 _isSome = true; 113 } 114 115 @property bool isSome() const { 116 return _isSome; 117 } 118 119 @property bool isNone() const { 120 return !_isSome; 121 } 122 123 void setNone() { 124 .destroy(_val); 125 _isSome = false; 126 } 127 128 void opAssign()(ValT val) { 129 _val = val; 130 _isSome = true; 131 } 132 } 133 134 // casting to type that have implicit cast available (e.g Option!int to Option!uint) 135 auto opCast(V : Option!U, U)() if (is(T : U)) { 136 return Option!U(val_); 137 } 138 139 @property ref inout(ValT) get() inout @safe pure nothrow 140 { 141 enum message = "Called `get' on none Option!" ~ T.stringof ~ "."; 142 assert(isSome, message); 143 return _val; 144 } 145 146 template toString() 147 { 148 import std.format : FormatSpec, formatValue; 149 // Needs to be a template because of DMD @@BUG@@ 13737. 150 void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) 151 { 152 if (isNull) 153 { 154 sink.formatValue("Option.none", fmt); 155 } 156 else 157 { 158 sink.formatValue(_value, fmt); 159 } 160 } 161 162 // Issue 14940 163 void toString()(scope void delegate(const(char)[]) @safe sink, FormatSpec!char fmt) 164 { 165 if (isNull) 166 { 167 sink.formatValue("Option.none", fmt); 168 } 169 else 170 { 171 sink.formatValue(_value, fmt); 172 } 173 } 174 } 175 176 // range interface 177 178 @property bool empty() const { 179 return isNone; 180 } 181 182 @property size_t length() const { 183 return isSome ? 1 : 0; 184 } 185 186 @property void popFront() { 187 setNone(); 188 } 189 190 static if (isNullAssignable!ValT) { 191 @property inout(ValT) front() inout { 192 return get; 193 } 194 @property Option!(inout(ValT)) save() inout { 195 return Option!(inout(ValT))(_val); 196 } 197 } 198 else { 199 @property ValT front() const { 200 return get; 201 } 202 203 @property Option!ValT save() const { 204 return isSome ? Option!ValT(_val) : none!T; 205 } 206 } 207 208 } 209 210 211 template isOption(T) 212 { 213 import std.traits : TemplateOf; 214 215 static if (__traits(compiles, TemplateOf!T)) { 216 enum isOption = __traits(isSame, TemplateOf!T, Option); 217 } 218 else { 219 enum isOption = false; 220 } 221 } 222 223 static assert (isOption!(Option!int)); 224 // static assert (isOption!(Option!Object)); 225 static assert (!isOption!(Object)); 226 227 228 /// execute fun with option value as parameter if option isSome 229 auto ifSome(alias fun, OptT)(OptT opt) { 230 static assert(isOption!OptT, "ifSome must be called with Option"); 231 if (opt.isSome) { 232 fun(opt.get); 233 } 234 return opt; 235 } 236 /// execute fun without parameter if option isNone 237 auto ifNone(alias fun, OptT)(OptT opt) { 238 static assert(isOption!OptT, "ifNone must be called with Option"); 239 if (opt.isNone) { 240 fun(); 241 } 242 return opt; 243 }