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 }