1 module jaster.cli.result; 2 3 import std.format : format; 4 import std.meta : AliasSeq; 5 6 /++ 7 + A basic result object use by various parts of JCLI. 8 + 9 + Params: 10 + T = The type that is returned by this result object. 11 + ++/ 12 struct Result(T) 13 { 14 // Can't use Algebraic as it's not nothrow, @nogc, and @safe. 15 // Not using a proper union as to simplify attribute stuff. 16 // Using an enum instead of `TypeInfo` as Object.opEquals has no attributes. 17 // All functions are templated to allow them to infer certain annoying attributes (e.g. for types that have a postblit). 18 static struct Success { static if(!is(T == void)) T value; } 19 static struct Failure { string error; } 20 21 private enum Type 22 { 23 ERROR, 24 Success, 25 Failure 26 } 27 private enum TypeToEnum(alias ResultType) = mixin("Type.%s".format(__traits(identifier, ResultType))); 28 private enum TypeToUnionAccess(alias ResultType) = "this._value.%s_".format(__traits(identifier, ResultType)); 29 30 private static struct ResultUnion 31 { 32 Success Success_; 33 Failure Failure_; 34 } 35 36 private Type _type; 37 private ResultUnion _value; 38 39 static foreach(ResultType; AliasSeq!(Success, Failure)) 40 { 41 /// 42 this()(ResultType value) 43 { 44 this._type = TypeToEnum!ResultType; 45 mixin(TypeToUnionAccess!ResultType ~ " = value;"); 46 } 47 48 mixin("alias is%s = isType!(%s);".format(__traits(identifier, ResultType), __traits(identifier, ResultType))); 49 mixin("alias as%s = asType!(%s);".format(__traits(identifier, ResultType), __traits(identifier, ResultType))); 50 } 51 52 /// 53 bool isType(ResultType)() 54 { 55 return this._type == TypeToEnum!ResultType; 56 } 57 58 /// 59 ResultType asType(ResultType)() 60 { 61 return mixin(TypeToUnionAccess!ResultType); 62 } 63 64 /// Constructs a successful result, returning the given value. 65 static Result!T success()(T value){ return typeof(this)(Success(value)); } 66 static if(is(T == void)) 67 static Result!void success()(){ return typeof(this)(Success()); } 68 69 /// Constructs a failure result, returning the given error. 70 static Result!T failure()(string error){ return typeof(this)(Failure(error)); } 71 72 /// Constructs a failure result if the `condition` is true, otherwise constructs a success result with the given `value`. 73 static Result!T failureIf()(bool condition, T value, string error) { return condition ? failure(error) : success(value); } 74 static if(is(T == void)) 75 static Result!T failureIf()(bool condition, string error) { return condition ? failure(error) : success(); } 76 } 77 78 void resultAssert(ResultT, ValueT)(ResultT result, ValueT expected) 79 { 80 assert(result.isSuccess, result.asFailure.error); 81 assert(result.asSuccess.value == expected); 82 } 83 84 void resultAssert(ResultT)(ResultT result) 85 { 86 assert(result.isSuccess, result.asFailure.error); 87 }