Skip to content

Commit

Permalink
feature: Add Result\ify()
Browse files Browse the repository at this point in the history
  • Loading branch information
mathroc committed Jul 19, 2023
1 parent 164c3e8 commit 0fe45f8
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
51 changes: 51 additions & 0 deletions src/functions/result.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,57 @@ function trap(callable $callback, string $exceptionClass = \Exception::class): R
}
}

/**
* Wrap a callable into one that transforms its returned value or thrown exception
* into a `Result` like `Result\trap()` does.
*
* # Examples
*
* Successful execution:
*
* ```
* self::assertEq(Result\ok(3), Result\ify(fn () => 3)());
* ```
*
* Checked exception:
*
* ```
* $x = Result\ify(fn () => new \DateTimeImmutable("2020-30-30 UTC"))();
* self::assertTrue($x->isErr());
* $x->unwrap();
* // @throws Exception Failed to parse time string (2020-30-30 UTC) at position 6 (0): Unexpected character
* ```
*
* Unchecked exception:
*
* ```
* Result\ify(fn () => 1/0)();
* // @throws DivisionByZeroError Division by zero
* ```
*
* @template U
* @param callable(...mixed):U $callback
* @return \Closure(...mixed): Result<U,\Throwable>
* @throws \Throwable
*/
#[ExamplesSetup(IgnoreUnusedResults::class)]

Check failure on line 130 in src/functions/result.php

View workflow job for this annotation

GitHub Actions / build (8.1)

Function TH\Maybe\Result\ify() has Throwable in PHPDoc @throws tag but it's not thrown.

Check failure on line 130 in src/functions/result.php

View workflow job for this annotation

GitHub Actions / build (8.1)

PHPDoc tag @param has invalid value (callable(...mixed):U $callback): Unexpected token "(", expected variable at offset 691

Check failure on line 130 in src/functions/result.php

View workflow job for this annotation

GitHub Actions / build (8.1)

Template type U of function TH\Maybe\Result\ify() is not referenced in a parameter.

Check failure on line 130 in src/functions/result.php

View workflow job for this annotation

GitHub Actions / build (8.2)

Function TH\Maybe\Result\ify() has Throwable in PHPDoc @throws tag but it's not thrown.

Check failure on line 130 in src/functions/result.php

View workflow job for this annotation

GitHub Actions / build (8.2)

PHPDoc tag @param has invalid value (callable(...mixed):U $callback): Unexpected token "(", expected variable at offset 691

Check failure on line 130 in src/functions/result.php

View workflow job for this annotation

GitHub Actions / build (8.2)

Template type U of function TH\Maybe\Result\ify() is not referenced in a parameter.
function ify(callable $callback, string $exceptionClass = \Exception::class): \Closure
{
return static function (...$args) use ($callback, $exceptionClass): Result
{
try {
/** @var Result<U,\Throwable> */
return Result\ok($callback(...$args));
} catch (\Throwable $th) {
if (\is_a($th, $exceptionClass)) {
return Result\err($th);
}

throw $th;
}
};
}

/**
* Converts from `Result<Result<T, E>, E>` to `Result<T, E>`.
*
Expand Down
48 changes: 48 additions & 0 deletions tests/Unit/Result/IfyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php declare(strict_types=1);

namespace TH\Maybe\Tests\Unit\Result;

use PHPUnit\Framework\TestCase;
use TH\Maybe\Result;
use TH\Maybe\Tests\Assert;
use TH\Maybe\Tests\Provider;

final class IfyTest extends TestCase
{
use Provider\Values;

/**
* @dataProvider values
*/
public function testIfyOk(mixed $value): void
{
$callback = static fn () => $value;

Assert::assertEquals($value, Result\ify($callback)()->unwrap());
}

public function testIfyCheckedException(): void
{
Assert::assertEquals(
new \Exception(
"Failed to parse time string (nope) at position 0 (n): The timezone could not be found in the database",
),
// @phpstan-ignore-next-line
Result\ify(static fn () => new \DateTimeImmutable("nope"))()->unwrapErr(),
);
}

public function testIfyUncheckedException(): void
{
try {
// @phpstan-ignore-next-line
Result\ify(static fn () => 1 / 0)();
Assert::fail("An exception should have been thrown");
} catch (\DivisionByZeroError $ex) {
Assert::assertEquals(
"Division by zero",
$ex->getMessage(),
);
}
}
}

0 comments on commit 0fe45f8

Please sign in to comment.