1 module jcli.core.result; 2 3 struct ResultOf(alias T) 4 { 5 import std.typecons : Nullable; 6 7 enum IsVoid = is(T == void); 8 alias This = typeof(this); 9 10 private 11 { 12 string _error = "I've not been initialised."; 13 int _errorCode; 14 static if(!IsVoid) 15 T _value; 16 } 17 18 static if(IsVoid) 19 { 20 static This ok()() 21 { 22 This t; 23 t._error = null; 24 return t; 25 } 26 } 27 else 28 { 29 static This ok()(T value) 30 { 31 This t; 32 t._error = null; 33 t._value = value; 34 return t; 35 } 36 } 37 38 static This fail()(string error, int errorCode = -1) 39 { 40 This t; 41 t._error = error; 42 t._errorCode = errorCode; 43 return t; 44 } 45 46 static if(!is(T == void) && is(T : Nullable!DataT, DataT)) 47 void opAssign(inout ResultOf!DataT notNullResult) 48 { 49 this._error = notNullResult._error; 50 this._errorCode = notNullResult._errorCode; 51 this._value = notNullResult._value; 52 } 53 54 inout: 55 56 bool isOk()() 57 { 58 return this._error is null; 59 } 60 61 bool isError()() 62 { 63 return this._error !is null; 64 } 65 66 string error()() 67 { 68 assert(!this.isOk, "Cannot call .error on an ok result. Please use .isOk to check."); 69 return this._error; 70 } 71 72 int errorCode()() 73 { 74 assert(!this.isOk, "Cannot call .errorCode on an ok result."); 75 return this._errorCode; 76 } 77 78 static if(!IsVoid) 79 inout(T) value()() inout 80 { 81 assert(this.isOk, "Cannot call .value on a failed result. Please use .isOk to check."); 82 return this._value; 83 } 84 85 void enforceOk()() 86 { 87 if(!this.isOk) 88 throw new ResultException(this.error, this.errorCode); 89 } 90 } 91 /// 92 unittest 93 { 94 auto ok = ResultOf!int.ok(1); 95 auto fail = ResultOf!int.fail("Bad", 200); 96 auto init = ResultOf!int.init; 97 auto void_ = Result.ok(); 98 99 assert(ok.isOk); 100 assert(ok.value == 1); 101 102 assert(!fail.isOk); 103 assert(fail.error == "Bad"); 104 assert(fail.errorCode == 200); 105 106 assert(!init.isOk); 107 assert(init.error); 108 109 assert(void_.isOk); 110 } 111 112 alias Result = ResultOf!void; 113 114 auto ok(T)(T value) 115 { 116 return ResultOf!T.ok(value); 117 } 118 119 auto ok()() 120 { 121 return Result.ok(); 122 } 123 124 auto fail(T)(string error, int errorCode = -1) 125 { 126 return ResultOf!T.fail(error, errorCode); 127 } 128 129 class ResultException : Exception 130 { 131 const(int) errorCode; 132 133 @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) 134 { 135 this.errorCode = -1; 136 super(msg, file, line, nextInChain); 137 } 138 139 @nogc @safe pure nothrow this(string msg, int errorCode, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) 140 { 141 this.errorCode = errorCode; 142 super(msg, file, line, nextInChain); 143 } 144 145 @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) 146 { 147 this.errorCode = -1; 148 super(msg, file, line, nextInChain); 149 } 150 }