1 // Scriptlike: Utility to aid in script-like programs. 2 // Written in the D programming language. 3 4 /// Copyright: Copyright (C) 2014-2015 Nick Sabalausky 5 /// License: $(LINK2 https://github.com/Abscissa/scriptlike/blob/master/LICENSE.txt, zlib/libpng) 6 /// Authors: Nick Sabalausky 7 8 module scriptlike.fail; 9 10 import std.conv; 11 import std.file; 12 import std.path; 13 import std.traits; 14 15 // Throwable.toString(sink) isn't an override on DMD 2.064.2, and druntime 16 // won't even call any Throwable.toString on DMD 2.064.2 anyway, so use 17 // a fallback method if Throwable doesn't have toString(sink). 18 static if( MemberFunctionsTuple!(Throwable, "toString").length > 1 ) 19 private enum useFallback = false; 20 else 21 private enum useFallback = true; 22 23 /// This is the exception thrown by fail(). There's no need to create or throw 24 /// this directly, but it's public in case you have reason to catch it. 25 class Fail : Exception 26 { 27 private this() 28 { 29 super(null); 30 } 31 32 private static string msg; 33 private static Fail opCall(string msg, string file=__FILE__, int line=__LINE__) 34 { 35 Fail.msg = msg; 36 throw cast(Fail) cast(void*) Fail.classinfo.init; 37 } 38 39 private static string fullMessage(string msg = Fail.msg) 40 { 41 auto appName = thisExePath().baseName(); 42 43 version(Windows) 44 appName = appName.stripExtension(); 45 46 return appName~": ERROR: "~msg; 47 } 48 49 static if(!useFallback) 50 { 51 override void toString(scope void delegate(in char[]) sink) const 52 { 53 sink(fullMessage()); 54 } 55 } 56 } 57 58 /++ 59 Call this to end your program with an error message for the user, and no 60 ugly stack trace. The error message is sent to stderr and the errorlevel is 61 set to non-zero. 62 63 This is exception-safe, all cleanup code gets run. 64 65 Your program's name is automatically detected from $(STD_FILE thisExePath). 66 67 Note, on DMD 2.064.2, the error message is displayed BEFORE the exception is 68 thrown. So if you catch the Fail exception, the message will have already been 69 displayed. This is due to limitations in the older druntime, and is fixed 70 on DMD 2.065 and up. 71 72 Example: 73 ---------------- 74 auto id = 3; 75 fail("You forgot to provide a destination for id #", id, "!"); 76 77 // Output on DMD 2.065 and up: 78 // yourProgramName: ERROR: You forgot to provide a destination for id #3! 79 80 // Output on DMD 2.064.2: 81 // yourProgramName: ERROR: You forgot to provide a destination for id #3! 82 // scriptlike.fail.Fail 83 ---------------- 84 +/ 85 void fail(T...)(T args) 86 { 87 static if(useFallback) 88 { 89 import std.stdio; 90 stderr.writeln(Fail.fullMessage( text(args) )); 91 stderr.flush(); 92 } 93 94 throw Fail( text(args) ); 95 } 96 97 /++ 98 Calls fail() if the condition is false. 99 100 This is much like $(FULL_STD_EXCEPTION enforce), but for for fail() instead of 101 arbitrary exceptions. 102 103 Example: 104 ---------------- 105 failEnforce(brokenSquareRoot(4)==2, "Reality broke! Expected 2, not ", brokenSquareRoot(4)); 106 107 // Output on DMD 2.065 and up: 108 // yourProgramName: ERROR: Reality broke! Expected 2, not 555 109 110 // Output on DMD 2.064.2: 111 // yourProgramName: ERROR: Reality broke! Expected 2, not 555 112 // scriptlike.fail.Fail 113 ---------------- 114 +/ 115 void failEnforce(T...)(bool cond, T args) 116 { 117 if(!cond) 118 fail(args); 119 }