|
3 | 3 | namespace TH\Maybe\Result; |
4 | 4 |
|
5 | 5 | use TH\DocTest\Attributes\ExamplesSetup; |
| 6 | +use TH\Maybe\Internal; |
6 | 7 | use TH\Maybe\Option; |
7 | 8 | use TH\Maybe\Result; |
8 | 9 | use TH\Maybe\Tests\Helpers\IgnoreUnusedResults; |
@@ -79,24 +80,93 @@ function err(mixed $value): Result\Err |
79 | 80 | * @template E of \Throwable |
80 | 81 | * @param callable(mixed...):U $callback |
81 | 82 | * @param class-string<E> $exceptionClass |
| 83 | + * @param class-string<E> $additionalExceptionClasses |
82 | 84 | * @return Result<U,E> |
83 | 85 | * @throws \Throwable |
84 | 86 | */ |
85 | 87 | #[ExamplesSetup(IgnoreUnusedResults::class)] |
86 | | -function trap(callable $callback, string $exceptionClass = \Exception::class): Result |
87 | | -{ |
| 88 | +function trap( |
| 89 | + callable $callback, |
| 90 | + string $exceptionClass = \Exception::class, |
| 91 | + string ...$additionalExceptionClasses, |
| 92 | +): Result { |
88 | 93 | try { |
89 | 94 | /** @var Result<U,E> */ |
90 | 95 | return Result\ok($callback()); |
91 | 96 | } catch (\Throwable $th) { |
92 | | - if (\is_a($th, $exceptionClass)) { |
93 | | - return Result\err($th); |
94 | | - } |
95 | | - |
96 | | - throw $th; |
| 97 | + /** @var Result\Err<E> */ |
| 98 | + return Internal\trap($th, Result\err(...), $exceptionClass, ...$additionalExceptionClasses); |
97 | 99 | } |
98 | 100 | } |
99 | 101 |
|
| 102 | +/** |
| 103 | + * Wrap a callable into one that transforms its returned value or thrown exception |
| 104 | + * into a `Result` like `Result\trap()` does, but without executing it. |
| 105 | + * |
| 106 | + * # Examples |
| 107 | + * |
| 108 | + * Successful execution: |
| 109 | + * |
| 110 | + * ``` |
| 111 | + * self::assertEq(Result\ok(3), Result\ify(fn () => 3)()); |
| 112 | + * ``` |
| 113 | + * |
| 114 | + * Checked exception: |
| 115 | + * |
| 116 | + * ``` |
| 117 | + * $x = Result\ify(fn () => new \DateTimeImmutable("2020-30-30 UTC"))(); |
| 118 | + * self::assertTrue($x->isErr()); |
| 119 | + * $x->unwrap(); |
| 120 | + * // @throws Exception Failed to parse time string (2020-30-30 UTC) at position 6 (0): Unexpected character |
| 121 | + * ``` |
| 122 | + * |
| 123 | + * Unchecked exception: |
| 124 | + * |
| 125 | + * ``` |
| 126 | + * Result\ify(fn () => 1/0)(); |
| 127 | + * // @throws DivisionByZeroError Division by zero |
| 128 | + * ``` |
| 129 | + * |
| 130 | + * Result-ify `strtotime()`: |
| 131 | + * |
| 132 | + * ``` |
| 133 | + * $strtotime = Result\ify( |
| 134 | + * static fn (...$args) |
| 135 | + * => \strtotime(...$args) |
| 136 | + * ?: throw new \RuntimeException("Could not convert string to time"), |
| 137 | + * ); |
| 138 | + * |
| 139 | + * self::assertEq($strtotime("2015-09-21 UTC midnight")->unwrap(), 1442793600); |
| 140 | + * |
| 141 | + * $r = $strtotime("nope"); |
| 142 | + * self::assertTrue($r->isErr()); |
| 143 | + * $r->unwrap(); // @throws RuntimeException Could not convert string to time |
| 144 | + * ``` |
| 145 | + * |
| 146 | + * @template U |
| 147 | + * @template E of \Throwable |
| 148 | + * @param callable(mixed...):U $callback |
| 149 | + * @param class-string<E> $exceptionClass |
| 150 | + * @param class-string<E> $additionalExceptionClasses |
| 151 | + * @return \Closure(mixed...):Result<U,E> |
| 152 | + */ |
| 153 | +#[ExamplesSetup(IgnoreUnusedResults::class)] |
| 154 | +function ify( |
| 155 | + callable $callback, |
| 156 | + string $exceptionClass = \Exception::class, |
| 157 | + string ...$additionalExceptionClasses, |
| 158 | +): \Closure { |
| 159 | + return static function (...$args) use ($callback, $exceptionClass, $additionalExceptionClasses): Result |
| 160 | + { |
| 161 | + try { |
| 162 | + return Result\ok($callback(...$args)); |
| 163 | + } catch (\Throwable $th) { |
| 164 | + /** @var Result\Err<E> */ |
| 165 | + return Internal\trap($th, Result\err(...), $exceptionClass, ...$additionalExceptionClasses); |
| 166 | + } |
| 167 | + }; |
| 168 | +} |
| 169 | + |
100 | 170 | /** |
101 | 171 | * Converts from `Result<Result<T, E>, E>` to `Result<T, E>`. |
102 | 172 | * |
|
0 commit comments