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 }