mirror of
https://github.com/danog/endtoend-test-psl.git
synced 2024-11-30 04:39:48 +01:00
Merge pull request #16 from azjezz/asio
[Asio] introduce ResultOrException wrapper
This commit is contained in:
commit
c74da9d328
@ -16,6 +16,9 @@
|
||||
<testsuite name="Psl Arr">
|
||||
<directory>tests/Psl/Arr</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Psl Asio">
|
||||
<directory>tests/Psl/Asio</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Psl Collection">
|
||||
<directory>tests/Psl/Collection</directory>
|
||||
</testsuite>
|
||||
|
35
psalm.xml
35
psalm.xml
@ -13,4 +13,39 @@
|
||||
<file name="src/bootstrap.php"/>
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
|
||||
<issueHandlers>
|
||||
<LessSpecificReturnType errorLevel="error"/>
|
||||
|
||||
<DeprecatedMethod errorLevel="error"/>
|
||||
<DeprecatedProperty errorLevel="error"/>
|
||||
<DeprecatedClass errorLevel="error"/>
|
||||
<DeprecatedConstant errorLevel="error"/>
|
||||
<DeprecatedInterface errorLevel="error"/>
|
||||
<DeprecatedTrait errorLevel="error"/>
|
||||
|
||||
<ForbiddenCode errorLevel="suppress"/>
|
||||
|
||||
<InternalMethod errorLevel="suppress"/>
|
||||
<InternalProperty errorLevel="error"/>
|
||||
<InternalClass errorLevel="error"/>
|
||||
|
||||
<MissingClosureReturnType errorLevel="error"/>
|
||||
<MissingReturnType errorLevel="error"/>
|
||||
<MissingPropertyType errorLevel="error"/>
|
||||
<InvalidDocblock errorLevel="error"/>
|
||||
<MisplacedRequiredParam errorLevel="error"/>
|
||||
|
||||
<PropertyNotSetInConstructor errorLevel="suppress"/>
|
||||
<MissingConstructor errorLevel="error"/>
|
||||
<MissingClosureParamType errorLevel="error"/>
|
||||
<MissingParamType errorLevel="error"/>
|
||||
<RedundantCondition errorLevel="error"/>
|
||||
<DocblockTypeContradiction errorLevel="error"/>
|
||||
<RedundantConditionGivenDocblockType errorLevel="error"/>
|
||||
<RawObjectIteration errorLevel="error"/>
|
||||
<InvalidStringClass errorLevel="error"/>
|
||||
|
||||
<UnresolvableInclude errorLevel="suppress"/>
|
||||
</issueHandlers>
|
||||
</psalm>
|
||||
|
57
src/Psl/Asio/IResultOrExceptionWrapper.php
Normal file
57
src/Psl/Asio/IResultOrExceptionWrapper.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Psl\Asio;
|
||||
|
||||
use Psl;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Represents a result of operation that either has a successful result or the exception object if that operation failed.
|
||||
*
|
||||
* This is an interface. You get generally `IResultOrExceptionWrapper<T>` by calling `wrap<T>()`, passing in the `callable(): T`,
|
||||
* and a `WrappedResult<T>` or `WrappedException<Te>` is returned.
|
||||
*
|
||||
* @template T
|
||||
*/
|
||||
interface IResultOrExceptionWrapper
|
||||
{
|
||||
/**
|
||||
* Return the result of the operation, or throw underlying exception.
|
||||
*
|
||||
* - if the operation succeeded: return its result.
|
||||
* - if the operation failed: throw the exception inciting failure.
|
||||
*
|
||||
* @psalm-return T - The result of the operation upon success
|
||||
*/
|
||||
public function getResult();
|
||||
|
||||
/**
|
||||
* Return the underlying exception, or fail with a invariant violation exception exception.
|
||||
*
|
||||
* - if the operation succeeded: fails with a invariant violation exception.
|
||||
* - if the operation failed: returns the exception indicating failure.
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException - When the operation succeeded
|
||||
*/
|
||||
public function getException(): Exception;
|
||||
|
||||
/**
|
||||
* Indicates whether the operation associated with this wrapper existed normally.
|
||||
*
|
||||
* if `isSucceeded()` returns `true`, `isFailed()` returns false.
|
||||
*
|
||||
* @return bool - `true` if the operation succeeded; `false` otherwise
|
||||
*/
|
||||
public function isSucceeded(): bool;
|
||||
|
||||
/**
|
||||
* Indicates whether the operation associated with this wrapper exited abnormally via an exception of some sort.
|
||||
*
|
||||
* if `isFailed()` returns `true`, `isSucceeded()` returns false.
|
||||
*
|
||||
* @return bool - `true` if the operation failed; `false` otherwise
|
||||
*/
|
||||
public function isFailed(): bool;
|
||||
}
|
69
src/Psl/Asio/WrappedException.php
Normal file
69
src/Psl/Asio/WrappedException.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Psl\Asio;
|
||||
|
||||
use Psl;
|
||||
use Psl\Str;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Represents the result of failed operation.
|
||||
*
|
||||
* @template T
|
||||
* @template Te of Exception
|
||||
*
|
||||
* @implements IResultOrExceptionWrapper<T>
|
||||
*/
|
||||
final class WrappedException implements IResultOrExceptionWrapper
|
||||
{
|
||||
/**
|
||||
* @psalm-var Te
|
||||
*/
|
||||
private Exception $exception;
|
||||
|
||||
/**
|
||||
* @psalm-param Te $exception
|
||||
*/
|
||||
public function __construct(Exception $exception)
|
||||
{
|
||||
$this->exception = $exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since this is a failed result wrapper, this always throws the exception thrown during the operation.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getResult(): void
|
||||
{
|
||||
throw $this->exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since this is a failed result wrapper, this always returns the exception thrown during the operation.
|
||||
*
|
||||
* @psalm-return Te - The exception thrown during the operation.
|
||||
*/
|
||||
public function getException(): Exception
|
||||
{
|
||||
return $this->exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since this is a failed result wrapper, this always returns `false`.
|
||||
*/
|
||||
public function isSucceeded(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since this is a failed result wrapper, this always returns `true`.
|
||||
*/
|
||||
public function isFailed(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
75
src/Psl/Asio/WrappedResult.php
Normal file
75
src/Psl/Asio/WrappedResult.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Psl\Asio;
|
||||
|
||||
use Psl;
|
||||
use Psl\Str;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Represents the result of successful operation.
|
||||
*
|
||||
* @template T
|
||||
*
|
||||
* @implements IResultOrExceptionWrapper<T>
|
||||
*/
|
||||
final class WrappedResult implements IResultOrExceptionWrapper
|
||||
{
|
||||
/**
|
||||
* @psalm-var T
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @psalm-param T $value
|
||||
*/
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since this is a successful result wrapper, this always returns the actual result of the operation.
|
||||
*
|
||||
* @psalm-return T
|
||||
*/
|
||||
public function getResult()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since this is a successful result wrapper, this always throws a
|
||||
* `Psl\Exception\InvariantViolationException` saying that there was no exception thrown from the operation.
|
||||
*
|
||||
* @throws Psl\Exception\InvariantViolationException
|
||||
*
|
||||
* @psalm-return no-return
|
||||
*/
|
||||
public function getException(): Exception
|
||||
{
|
||||
Psl\invariant_violation('No exception thrown from the operation.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Since this is a successful result wrapper, this always returns `true`.
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function isSucceeded(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since this is a successful result wrapper, this always returns `false`.
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
public function isFailed(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
25
src/Psl/Asio/wrap.php
Normal file
25
src/Psl/Asio/wrap.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Psl\Asio;
|
||||
|
||||
/**
|
||||
* Wrap the given callable result in a `WrappedResult`, or `WrappedException` if the callable throws
|
||||
* an `Exception`.
|
||||
*
|
||||
* @template T
|
||||
*
|
||||
* @psalm-param (callable(): T) $fun
|
||||
*
|
||||
* @psalm-return IResultOrExceptionWrapper<T>
|
||||
*/
|
||||
function wrap(callable $fun): IResultOrExceptionWrapper
|
||||
{
|
||||
try {
|
||||
$result = $fun();
|
||||
return new WrappedResult($result);
|
||||
} catch (\Exception $e) {
|
||||
return new WrappedException($e);
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ namespace Psl\Str;
|
||||
* Str\format('%s is %d character(s) long.', 'س', Str\length('س'));
|
||||
* => Str('س is 1 character(s) long.')
|
||||
*
|
||||
* @psalm-param int|float|string|bool ...$args
|
||||
* @psalm-param int|float|string ...$args
|
||||
*
|
||||
* @return string a string produced according to the formatting string
|
||||
* format
|
||||
|
@ -60,3 +60,15 @@ function boolean($val): bool
|
||||
{
|
||||
return (bool) $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
function type($value): string
|
||||
{
|
||||
return \is_object($value) ? \get_class($value) : \gettype($value);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace Psl;
|
||||
|
||||
/**
|
||||
* @psalm-param int|float|string|bool ...$args
|
||||
* @psalm-param int|float|string ...$args
|
||||
*
|
||||
* @psalm-assert true $fact
|
||||
*/
|
||||
@ -17,9 +17,11 @@ function invariant(bool $fact, string $message, ...$args): void
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param int|float|string|bool ...$args
|
||||
* @psalm-param int|float|string ...$args
|
||||
*
|
||||
* @pslam-return no-return
|
||||
* @psalm-return no-return
|
||||
*
|
||||
* @throws Exception\InvariantViolationException
|
||||
*/
|
||||
function invariant_violation(string $format, ...$args): void
|
||||
{
|
||||
|
@ -11,6 +11,7 @@ function __bootstrap(): void
|
||||
{
|
||||
static $booted = false;
|
||||
static $files = [
|
||||
'Psl/Asio/wrap.php',
|
||||
'/Psl/Random/string.php',
|
||||
'/Psl/Random/int.php',
|
||||
'/Psl/Random/float.php',
|
||||
|
42
tests/Psl/Asio/WrapTest.php
Normal file
42
tests/Psl/Asio/WrapTest.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Psl\Tests\Asio;
|
||||
|
||||
use Psl\Asio;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psl\Exception\InvariantViolationException;
|
||||
|
||||
class WrapTest extends TestCase
|
||||
{
|
||||
public function testWrapException(): void
|
||||
{
|
||||
$exception = new \Exception('foo');
|
||||
$wrapper = Asio\wrap(static function() use ($exception): void {
|
||||
throw $exception;
|
||||
});
|
||||
$this->assertFalse($wrapper->isSucceeded());
|
||||
$this->assertTrue($wrapper->isFailed());
|
||||
$this->assertSame($exception, $wrapper->getException());
|
||||
|
||||
$this->expectExceptionObject($exception);
|
||||
|
||||
$wrapper->getResult();
|
||||
}
|
||||
|
||||
public function testWrapResult(): void
|
||||
{
|
||||
$wrapper = Asio\wrap(static function(): string {
|
||||
return 'foo';
|
||||
});
|
||||
$this->assertTrue($wrapper->isSucceeded());
|
||||
$this->assertFalse($wrapper->isFailed());
|
||||
$this->assertSame('foo', $wrapper->getResult());
|
||||
|
||||
$this->expectException(InvariantViolationException::class);
|
||||
$this->expectExceptionMessage('No exception thrown');
|
||||
|
||||
$wrapper->getException();
|
||||
}
|
||||
}
|
40
tests/Psl/Asio/WrappedExceptionTest.php
Normal file
40
tests/Psl/Asio/WrappedExceptionTest.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Psl\Tests\Asio;
|
||||
|
||||
use Psl\Asio\WrappedException;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class WrappedExceptionTest extends TestCase
|
||||
{
|
||||
public function testIsSucceeded(): void
|
||||
{
|
||||
$wrapper = new WrappedException(new \Exception('foo'));
|
||||
$this->assertFalse($wrapper->isSucceeded());
|
||||
}
|
||||
|
||||
public function testIsFailed(): void
|
||||
{
|
||||
$wrapper = new WrappedException(new \Exception('foo'));
|
||||
$this->assertTrue($wrapper->isFailed());
|
||||
}
|
||||
|
||||
public function testGetResult(): void
|
||||
{
|
||||
$exception = new \Exception('bar');
|
||||
$wrapper = new WrappedException($exception);
|
||||
|
||||
$this->expectExceptionObject($exception);
|
||||
$wrapper->getResult();
|
||||
}
|
||||
|
||||
public function testGetException(): void
|
||||
{
|
||||
$exception = new \Exception('bar');
|
||||
$wrapper = new WrappedException($exception);
|
||||
$e = $wrapper->getException();
|
||||
$this->assertSame($exception, $e);
|
||||
}
|
||||
}
|
39
tests/Psl/Asio/WrappedResultTest.php
Normal file
39
tests/Psl/Asio/WrappedResultTest.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Psl\Tests\Asio;
|
||||
|
||||
use Psl\Asio\WrappedResult;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psl\Exception\InvariantViolationException;
|
||||
|
||||
class WrappedResultTest extends TestCase
|
||||
{
|
||||
public function testIsSucceeded(): void
|
||||
{
|
||||
$wrapper = new WrappedResult('hello');
|
||||
$this->assertTrue($wrapper->isSucceeded());
|
||||
}
|
||||
|
||||
public function testIsFailed(): void
|
||||
{
|
||||
$wrapper = new WrappedResult('hello');
|
||||
$this->assertFalse($wrapper->isFailed());
|
||||
}
|
||||
|
||||
public function testGetResult(): void
|
||||
{
|
||||
$wrapper = new WrappedResult('hello');
|
||||
$this->assertSame('hello', $wrapper->getResult());
|
||||
}
|
||||
|
||||
public function testGetException(): void
|
||||
{
|
||||
$wrapper = new WrappedResult('hello');
|
||||
|
||||
$this->expectException(InvariantViolationException::class);
|
||||
$this->expectExceptionMessage('No exception thrown');
|
||||
|
||||
$wrapper->getException();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user