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