@@ -97,6 +97,73 @@ function trap(callable $callback, string $exceptionClass = \Exception::class): R
9797 }
9898}
9999
100+ /**
101+ * Wrap a callable into one that transforms its returned value or thrown exception
102+ * into a `Result` like `Result\trap()` does.
103+ *
104+ * # Examples
105+ *
106+ * Successful execution:
107+ *
108+ * ```
109+ * self::assertEq(Result\ok(3), Result\ify(fn () => 3)());
110+ * ```
111+ *
112+ * Checked exception:
113+ *
114+ * ```
115+ * $x = Result\ify(fn () => new \DateTimeImmutable("2020-30-30 UTC"))();
116+ * self::assertTrue($x->isErr());
117+ * $x->unwrap();
118+ * // @throws Exception Failed to parse time string (2020-30-30 UTC) at position 6 (0): Unexpected character
119+ * ```
120+ *
121+ * Unchecked exception:
122+ *
123+ * ```
124+ * Result\ify(fn () => 1/0)();
125+ * // @throws DivisionByZeroError Division by zero
126+ * ```
127+ *
128+ * Result-ify `strtotime()`:
129+ *
130+ * ```
131+ * $strtotime = Result\ify(
132+ * static fn (...$args)
133+ * => \strtotime(...$args)
134+ * ?: throw new \RuntimeException("Could not convert string to time"),
135+ * );
136+ *
137+ * self::assertEq($strtotime("2015-09-21 UTC midnight")->unwrap(), 1442793600);
138+ *
139+ * $r = $strtotime("nope");
140+ * self::assertTrue($r->isErr());
141+ * $r->unwrap(); // @throws RuntimeException Could not convert string to time
142+ * ```
143+ *
144+ * @template U
145+ * @template E of \Throwable
146+ * @param callable(mixed...):U $callback
147+ * @param class-string<E> $exceptionClass
148+ * @return \Closure(mixed...):Result<U,E>
149+ */
150+ #[ExamplesSetup(IgnoreUnusedResults::class)]
151+ function ify (callable $ callback , string $ exceptionClass = \Exception::class): \Closure
152+ {
153+ return static function (...$ args ) use ($ callback , $ exceptionClass ): Result
154+ {
155+ try {
156+ return Result \ok ($ callback (...$ args ));
157+ } catch (\Throwable $ th ) {
158+ if (\is_a ($ th , $ exceptionClass )) {
159+ return Result \err ($ th );
160+ }
161+
162+ throw $ th ;
163+ }
164+ };
165+ }
166+
100167/**
101168 * Converts from `Result<Result<T, E>, E>` to `Result<T, E>`.
102169 *
0 commit comments