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 }