1 /**
2  * Exception formatting
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.exception;
15 
16 import std.algorithm;
17 import std.string;
18 
19 string formatException(Throwable e)
20 {
21 	string[] descriptions;
22 	while (e)
23 		descriptions ~= e.toString(),
24 		e = e.next;
25 	return descriptions.join("\n===================================\n");
26 }
27 
28 // --------------------------------------------------------------------------
29 
30 import ae.utils.meta;
31 
32 mixin template DeclareException(string NAME, BASE = Exception)
33 {
34 	mixin(mixin(X!q{
35 		class @(NAME) : Exception
36 		{
37 			this(string s, string fn = __FILE__, size_t ln = __LINE__)
38 			{
39 				super(s, fn, ln);
40 			}
41 		}
42 	}));
43 }
44 
45 unittest
46 {
47 	mixin DeclareException!q{OutOfCheeseException};
48 	try
49 		throw new OutOfCheeseException("*** OUT OF CHEESE ***");
50 	catch (Exception e)
51 		assert(e.classinfo.name.indexOf("Cheese") > 0);
52 }
53 
54 /// This exception can never be thrown.
55 /// Useful for a temporary or aliased catch block exception type.
56 class NoException : Exception
57 {
58 	@disable this()
59 	{
60 		super(null);
61 	}
62 }
63 
64 // --------------------------------------------------------------------------
65 
66 import std.conv;
67 
68 /// Returns string mixin for adding a chained exception
69 string exceptionContext(string messageExpr, string name = text(__LINE__))
70 {
71 	name = "exceptionContext_" ~ name;
72 	return mixin(X!q{
73 		bool @(name);
74 		scope(exit) if (@(name)) throw new Exception(@(messageExpr));
75 		scope(failure) @(name) = true;
76 	});
77 }
78 
79 unittest
80 {
81 	try
82 	{
83 		mixin(exceptionContext(q{"Second"}));
84 		throw new Exception("First");
85 	}
86 	catch (Exception e)
87 	{
88 		assert(e.msg == "First");
89 		assert(e.next.msg == "Second");
90 	}
91 }
92 
93 // --------------------------------------------------------------------------
94 
95 string[] getStackTrace(string until = __FUNCTION__, string since = "_d_run_main")
96 {
97 	string[] lines;
98 	try
99 		throw new Exception(null);
100 	catch (Exception e)
101 		lines = e.toString().splitLines()[1..$];
102 
103 	auto start = lines.countUntil!(line => line.canFind(until));
104 	auto end   = lines.countUntil!(line => line.canFind(since));
105 	if (start < 0) start = -1;
106 	if (end   < 0) end   = lines.length;
107 	return lines[start+1..end];
108 }