1 module exceptionhandling; 2 3 private { 4 import std.math : approxEqual; 5 bool cmpFloat(T)(T tt, T tc) { 6 return approxEqual(tt, tc); 7 } 8 9 bool cmpFloatNot(T)(T tt, T tc) { 10 return !approxEqual(tt, tc); 11 } 12 13 bool cmpRest(T)(T tt, T tc) { 14 return tt == tc; 15 } 16 17 bool cmpRestNot(T)(T tt, T tc) { 18 return tt == tc; 19 } 20 } 21 22 /** Assert that `toTest` is equal to `toCompareAgainst`. 23 If `T` is a floating point `approxEqual` is used to compare the values. 24 `toTest` is returned if the comparision is correct. 25 If the comparision is incorrect an Exception is thrown. If assertEqual is used 26 in a unittest block an AssertError is thrown an Exception otherwise. 27 */ 28 ref T assertEqual(T)(auto ref T toTest, auto ref T toCompareAgainst, 29 const string file = __FILE__, const int line = __LINE__) 30 { 31 import std.traits : isFloatingPoint; 32 static if(isFloatingPoint!T) { 33 return AssertImpl!(T, cmpFloat)(toTest, toCompareAgainst, file, line); 34 } else { 35 return AssertImpl!(T, cmp)(toTest, toCompareAgainst, file, line); 36 } 37 } 38 39 /// ditto 40 ref T assertNotEqual(T)(auto ref T toTest, auto ref T toCompareAgainst, 41 const string file = __FILE__, const int line = __LINE__) 42 { 43 import std.traits : isFloatingPoint; 44 static if(isFloatingPoint!T) { 45 return AssertImpl!(T, cmpFloatNot)(toTest, toCompareAgainst, file, line); 46 } else { 47 return AssertImpl!(T, cmpNot)(toTest, toCompareAgainst, file, line); 48 } 49 } 50 51 private ref T AssertImpl(T,alias Cmp)(auto ref T toTest, auto ref T toCompareAgainst, 52 const string file, const int line) 53 { 54 import std.format : format; 55 version(unittest) { 56 import core.exception : AssertError; 57 alias ExceptionType = AssertError; 58 } else { 59 alias ExceptionType = Exception; 60 } 61 62 bool cmpRslt = false; 63 try { 64 cmpRslt = Cmp(toTest, toCompareAgainst); 65 } catch(ExceptionType e) { 66 throw new ExceptionType( 67 format("Exception thrown while \"toTest(%s) != toCompareAgainst(%s)\"", 68 toTest, toCompareAgainst), file, line, e 69 ); 70 } 71 72 if(!cmpRslt) { 73 throw new ExceptionType(format("toTest(%s) != toCompareAgainst(%s)", 74 toTest, toCompareAgainst), file, line 75 ); 76 } 77 return toTest; 78 } 79 80 unittest { 81 import core.exception : AssertError; 82 import std.exception : assertThrown; 83 assertEqual(1.0, 1.0); 84 85 assertThrown!AssertError(assertEqual(1.0, 0.0)); 86 } 87 88 /** Calls `exp` if `exp` does not throw the return value from `exp` is 89 returned, if `exp` throws the Exception is cought, a new Exception is 90 constructed with a message made of `args` space seperated and the previously 91 cought exception is nested in the newly created exception. 92 */ 93 auto chain(ET = Exception, F, int line = __LINE__, string file = __FILE__, Args...) 94 (lazy F exp, lazy Args args) 95 { 96 try { 97 return exp(); 98 } catch(Exception e) { 99 throw new ET(joinElem(args), file, line, e); 100 } 101 } 102 103 auto expect(ET = Exception, F, int line = __LINE__, string file = __FILE__, Args...) 104 (lazy F exp, lazy Args args) 105 { 106 try { 107 return exp(); 108 } catch(Exception e) { 109 throw new ET(joinElem(args), file, line, e); 110 } 111 } 112 113 private string joinElem(Args...)(lazy Args args) { 114 import std.array : appender; 115 import std.format : formattedWrite; 116 117 auto app = appender!string(); 118 foreach(arg; args) { 119 formattedWrite(app, "%s ", arg); 120 } 121 return app.data; 122 }