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 }