1 // Scriptlike: Utility to aid in script-like programs.
2 // Written in the D programming language.
3 
4 /// Copyright: Copyright (C) 2014-2017 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 /// This is the exception thrown by fail(). There's no need to create or throw
16 /// this directly, but it's public in case you have reason to catch it.
17 class Fail : Exception
18 {
19 	private this()
20 	{
21 		super(null);
22 	}
23 	
24 	private static string msg;
25 	private static Fail opCall(string msg, string file=__FILE__, int line=__LINE__)
26 	{
27 		Fail.msg = msg;
28 		static if(__traits(compiles, Fail.classinfo.initializer))
29 			// DMD 2.072 or 2.073 deprecates 'classinfo.init'
30 			throw cast(Fail) cast(void*) Fail.classinfo.initializer;
31 		else
32 			// DMD 2.069.2 and below lack 'classinfo.initializer'
33 			throw cast(Fail) cast(void*) Fail.classinfo.init;
34 	}
35 	
36 	private static string fullMessage(string msg = Fail.msg)
37 	{
38 		auto appName = thisExePath().baseName();
39 
40 		version(Windows)
41 			appName = appName.stripExtension();
42 
43 		return appName~": ERROR: "~msg;
44 	}
45 	
46 	override void toString(scope void delegate(in char[]) sink) const
47 	{
48 		sink(fullMessage());
49 	}
50 }
51 
52 /++
53 Call this to end your program with an error message for the user, and no
54 ugly stack trace. The error message is sent to stderr and the errorlevel is
55 set to non-zero.
56 
57 This is exception-safe, all cleanup code gets run.
58 
59 Your program's name is automatically detected from $(STD_FILE thisExePath).
60 
61 Example:
62 ----------------
63 auto id = 3;
64 fail("You forgot to provide a destination for id #", id, "!");
65 
66 // Output:
67 // yourProgramName: ERROR: You forgot to provide a destination for id #3!
68 ----------------
69 +/
70 void fail(T...)(T args)
71 {
72 	throw Fail( text(args) );
73 }
74 
75 /++
76 Calls fail() if the condition is false.
77 
78 This is much like $(FULL_STD_EXCEPTION enforce), but for for fail() instead of
79 arbitrary exceptions.
80 
81 Example:
82 ----------------
83 failEnforce(brokenSquareRoot(4)==2, "Reality broke! Expected 2, not ", brokenSquareRoot(4));
84 
85 // Output:
86 // yourProgramName: ERROR: Reality broke! Expected 2, not 555
87 ----------------
88 +/
89 void failEnforce(T...)(bool cond, T args)
90 {
91 	if(!cond)
92 		fail(args);
93 }