1 /** 2 Functional try 3 */ 4 module ddash.functional.try_; 5 6 /// 7 unittest { 8 import std.algorithm: map, each; 9 10 auto arr = [1, 2, 3]; 11 12 int f(int i) { 13 if (i % 2 == 1) { 14 throw new Exception("NOT EVEN!!!"); 15 } 16 return i; 17 } 18 19 auto result = arr 20 .map!(try_!f) 21 .map!(r => r 22 .match!( 23 (int _) => "even", 24 (Exception _) => "odd" 25 ) 26 ); 27 28 assert(result.equal(["odd", "even", "odd"])); 29 } 30 31 import ddash.common; 32 33 /** 34 Creates a Try range out of an alias to a function that could throw. 35 36 Executing a Try range will produce a `Expected!(T, Exception)` 37 38 You may also call `.match` directly on the try range. 39 40 See_Also: 41 `try_` 42 43 Since: 44 0.0.1 45 */ 46 struct Try(alias fun) { 47 import ddash.utils.expect; 48 import ddash.lang.types: isVoid; 49 50 bool empty = false; 51 bool resolved = false; 52 53 alias T = Expect!(typeof(fun()), Exception); 54 55 private T result; 56 57 private void resolve() { 58 if (resolved) { 59 return; 60 } 61 scope(exit) resolved = true; 62 try { 63 static if (isVoid!(T.Expected)) { 64 fun(); 65 } else { 66 result = T.expected(fun()); 67 } 68 } catch (Exception ex) { 69 result = unexpected(ex); 70 } 71 } 72 73 @property T front() { 74 resolve; 75 return result; 76 } 77 78 void popFront() nothrow { 79 scope(exit) empty = true; 80 resolve; 81 } 82 83 /** 84 Pass two lambdas to the match function. The first one handles the success case 85 and the second one handles the failure case. 86 87 Calling match will execute the try function if it has not already done so 88 89 Params: 90 handlers = lamda that handles the success case 91 handlers = lambda that handles the exception 92 93 Returns: 94 Whatever the lambas return 95 */ 96 auto match(handlers...)() { 97 resolve; 98 return result.match!( 99 (T.Expected t) { 100 static if (isVoid!(T.Expected)) { 101 return handlers[0](); 102 } else { 103 return handlers[0](t); 104 } 105 }, 106 (T.Unexpected ex) { 107 return handlers[1](ex); 108 } 109 ); 110 } 111 } 112 113 /** 114 Creates a range expression out of a throwing functions 115 116 Since: 117 - 0.0.1 118 */ 119 template try_(alias func) { 120 auto try_(Args...)(auto ref Args args) { 121 return Try!(() => func(args))(); 122 } 123 } 124 125 /// 126 unittest { 127 import std.typecons: Flag; 128 129 void f0(Flag!"throws" throws) { 130 if (throws) { 131 throw new Exception("f0"); 132 } 133 } 134 int f1(Flag!"throws" throws) { 135 if (throws) { 136 throw new Exception("f1"); 137 } 138 return 0; 139 } 140 141 auto f0_throws = try_!f0(Yes.throws); 142 auto f0_nothrows = try_!f0(No.throws); 143 144 auto f1_throws = try_!f1(Yes.throws); 145 auto f1_nothrows = try_!f1(No.throws); 146 147 auto g() { 148 try { 149 throw new Exception("hahah"); 150 } catch (Exception ex) { 151 return ex; 152 } 153 } 154 155 assert(!f0_throws.front.isExpected); 156 assert( f0_nothrows.front.isExpected); 157 assert(!f1_throws.front.isExpected); 158 assert( f1_nothrows.front.isExpected); 159 }