mirror of
https://github.com/danog/endtoend-test-psl.git
synced 2024-12-02 09:38:32 +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">
|
<testsuite name="Psl Arr">
|
||||||
<directory>tests/Psl/Arr</directory>
|
<directory>tests/Psl/Arr</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
|
<testsuite name="Psl Asio">
|
||||||
|
<directory>tests/Psl/Asio</directory>
|
||||||
|
</testsuite>
|
||||||
<testsuite name="Psl Collection">
|
<testsuite name="Psl Collection">
|
||||||
<directory>tests/Psl/Collection</directory>
|
<directory>tests/Psl/Collection</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
|
35
psalm.xml
35
psalm.xml
@ -13,4 +13,39 @@
|
|||||||
<file name="src/bootstrap.php"/>
|
<file name="src/bootstrap.php"/>
|
||||||
</ignoreFiles>
|
</ignoreFiles>
|
||||||
</projectFiles>
|
</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>
|
</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\format('%s is %d character(s) long.', 'س', Str\length('س'));
|
||||||
* => Str('س is 1 character(s) long.')
|
* => 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
|
* @return string a string produced according to the formatting string
|
||||||
* format
|
* format
|
||||||
|
@ -60,3 +60,15 @@ function boolean($val): bool
|
|||||||
{
|
{
|
||||||
return (bool) $val;
|
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;
|
namespace Psl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @psalm-param int|float|string|bool ...$args
|
* @psalm-param int|float|string ...$args
|
||||||
*
|
*
|
||||||
* @psalm-assert true $fact
|
* @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
|
function invariant_violation(string $format, ...$args): void
|
||||||
{
|
{
|
||||||
|
@ -11,6 +11,7 @@ function __bootstrap(): void
|
|||||||
{
|
{
|
||||||
static $booted = false;
|
static $booted = false;
|
||||||
static $files = [
|
static $files = [
|
||||||
|
'Psl/Asio/wrap.php',
|
||||||
'/Psl/Random/string.php',
|
'/Psl/Random/string.php',
|
||||||
'/Psl/Random/int.php',
|
'/Psl/Random/int.php',
|
||||||
'/Psl/Random/float.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