1 module result;
2 
3 import std.stdio;
4 import std.format           : format;
5 import core.attribute       : mustuse;
6 import std.traits           : ReturnType, isSomeFunction;
7 
8 // TODO: check if is enum 
9 mixin template injectEnum(EnumType)
10 {
11     private import std.format       : format;
12     private import std.traits       : EnumMembers;
13 
14     static foreach (member; EnumMembers!EnumType)
15         mixin(q{
16             enum typeof(this) %1$s = typeof(this)(%2$s.%1$s);
17         }.format(member.stringof, EnumType.stringof));
18 }
19 
20 private enum isFuncValidA(alias fnc, R) = __traits(compiles, fnc(R.init));
21 private enum isFuncValidB(alias fnc, R) =
22                 __traits(compiles, fnc(R.init, string.init, size_t.init));
23 
24 // T must have a default constructor
25 template Result(ErrorType, T)
26 {
27     @safe @nogc nothrow:
28 
29     @mustuse
30     struct Result
31     {
32         // use GLErrorInfo
33         ErrorType error;
34 
35         static if (!is(T == void)) {
36             T value;
37 
38             this(ErrorType error, T value)
39             {
40                 this.error = error;
41                 this.value = value;
42             }
43 
44             // TODO: somehow set to NO_ERROR
45             this(T value)
46             {
47                 this.error = ErrorType.no_error();
48                 this.value = value;
49             }
50         }
51 
52         this(ErrorType error) { this.error = error; }
53 
54 
55         bool is_error() const => this.error.is_error();
56         bool opCast(T: bool)() const => is_error();
57     }
58 }
59 
60 template ResultPartial(ErrorType)
61 {
62     alias ResultPartial(T) = Result!(ErrorType, T);
63 }
64 
65 T unwrap_or_else(alias fnc, E, T)(Result!(E, T) result)
66 if (isSomeFunction!fnc && isFuncValidA!(fnc, Result!(E, T)))
67 {
68     if (result.error.is_error()) {
69         static if (is(ReturnType!fnc == T))
70             return fnc(result);
71         else
72             fnc(result);
73     }
74 
75     static if (!is(T == void))
76         return result.value;
77 }
78 
79 T unwrap_or_else(alias fnc, E, T)(Result!(E, T) result)
80 if (!isSomeFunction!fnc && isFuncValidA!(fnc, Result!(E, T)))
81 {
82     return unwrap_or_else!(fnc!(Result!(E, T)), E, T)(result);
83 }
84 
85 T unwrap_or_else(alias fnc, E, T)(Result!(E, T) result, string file = __FILE__, size_t line = __LINE__)
86 if (isSomeFunction!fnc && isFuncValidB!(fnc, typeof(result)))
87 {
88     if (result.error.is_error()) {
89         static if (is(ReturnType!fnc == T))
90             return fnc(result, file, line);
91         else
92             fnc(result, file, line);
93     }
94     static if (!is(T == void))
95         return result.value;
96 }
97 
98 T unwrap_or_else(alias fnc, E, T)(Result!(E, T) result, string file = __FILE__, size_t line = __LINE__)
99 if (!isSomeFunction!fnc && isFuncValidB!(fnc, typeof(result)))
100 {
101     return unwrap_or_else!(fnc!(typeof(result)), E, T)(result, file, line);
102 }
103 
104 void throw_on_error_(E, T)(Result!(E, T) res, string file = __FILE__, size_t line = __LINE__)
105 {
106     throw new Exception(res.error.to_error_msg(), file, line);
107 }
108 
109 T throw_on_error(E, T)(Result!(E, T) result, string file = __FILE__, size_t line = __LINE__)
110     => result.unwrap_or_else!(throw_on_error_!(E, T), E, T)(file, line);
111 
112 // Maybe use core.stdc.assert
113 /* T unwrap_or_assert(T)(GLResult!T res, string file = __FILE__, size_t line = __LINE__) */
114 /* { */
115 /*     import core.stdc.assert_; */
116 /*     import std.format           : format; */
117 /*     import std.string           : toStringz; */
118 
119 /*     return unwrap_or_else!( */
120 /*         (res, file, line) => assert(res.error) */
121 /*     )(res, file, line); */
122 /* } */
123 
124 template match(handlers...)
125 {
126     import std.sumtype;
127 
128     auto match(E, T)(ref Result!(E, T) result)
129     {
130         static if (is(T == void))
131             alias ResSum = SumType!(E);
132         else
133             alias ResSum = SumType!(E, T);
134 
135         if (result.is_error())
136             return std.sumtype.match!(handlers)(ResSum(result.error));
137         else
138             return std.sumtype.match!(handlers)(ResSum(result.value));
139     }
140 }