mirror of
https://github.com/danog/endtoend-test-psl.git
synced 2024-11-30 04:39:48 +01:00
[Type] add positive-int type (#177)
This commit is contained in:
parent
eee8390be7
commit
963a0bb6fd
@ -45,6 +45,7 @@
|
|||||||
- [num](./../../src/Psl/Type/num.php#L10)
|
- [num](./../../src/Psl/Type/num.php#L10)
|
||||||
- [object](./../../src/Psl/Type/object.php#L14)
|
- [object](./../../src/Psl/Type/object.php#L14)
|
||||||
- [optional](./../../src/Psl/Type/optional.php#L14)
|
- [optional](./../../src/Psl/Type/optional.php#L14)
|
||||||
|
- [positive_int](./../../src/Psl/Type/positive_int.php#L10)
|
||||||
- [resource](./../../src/Psl/Type/resource.php#L12)
|
- [resource](./../../src/Psl/Type/resource.php#L12)
|
||||||
- [scalar](./../../src/Psl/Type/scalar.php#L10)
|
- [scalar](./../../src/Psl/Type/scalar.php#L10)
|
||||||
- [shape](./../../src/Psl/Type/shape.php#L15)
|
- [shape](./../../src/Psl/Type/shape.php#L15)
|
||||||
|
@ -392,6 +392,7 @@ final class Loader
|
|||||||
'Psl\Type\null',
|
'Psl\Type\null',
|
||||||
'Psl\Type\nullable',
|
'Psl\Type\nullable',
|
||||||
'Psl\Type\optional',
|
'Psl\Type\optional',
|
||||||
|
'Psl\Type\positive_int',
|
||||||
'Psl\Type\num',
|
'Psl\Type\num',
|
||||||
'Psl\Type\object',
|
'Psl\Type\object',
|
||||||
'Psl\Type\resource',
|
'Psl\Type\resource',
|
||||||
@ -595,6 +596,7 @@ final class Loader
|
|||||||
'Psl\Type\Internal\NullType',
|
'Psl\Type\Internal\NullType',
|
||||||
'Psl\Type\Internal\NullableType',
|
'Psl\Type\Internal\NullableType',
|
||||||
'Psl\Type\Internal\OptionalType',
|
'Psl\Type\Internal\OptionalType',
|
||||||
|
'Psl\Type\Internal\PositiveIntType',
|
||||||
'Psl\Type\Internal\NumType',
|
'Psl\Type\Internal\NumType',
|
||||||
'Psl\Type\Internal\ObjectType',
|
'Psl\Type\Internal\ObjectType',
|
||||||
'Psl\Type\Internal\ResourceType',
|
'Psl\Type\Internal\ResourceType',
|
||||||
|
99
src/Psl/Type/Internal/PositiveIntType.php
Normal file
99
src/Psl/Type/Internal/PositiveIntType.php
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Psl\Type\Internal;
|
||||||
|
|
||||||
|
use Psl\Str;
|
||||||
|
use Psl\Type;
|
||||||
|
use Psl\Type\Exception\AssertException;
|
||||||
|
use Psl\Type\Exception\CoercionException;
|
||||||
|
|
||||||
|
use function is_float;
|
||||||
|
use function is_int;
|
||||||
|
use function is_object;
|
||||||
|
use function is_string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends Type\Type<positive-int>
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class PositiveIntType extends Type\Type
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
*
|
||||||
|
* @psalm-assert-if-true positive-int $value
|
||||||
|
*/
|
||||||
|
public function matches($value): bool
|
||||||
|
{
|
||||||
|
return is_int($value) && $value > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
*
|
||||||
|
* @throws CoercionException
|
||||||
|
*
|
||||||
|
* @return positive-int
|
||||||
|
*/
|
||||||
|
public function coerce($value): int
|
||||||
|
{
|
||||||
|
if (is_int($value) && $value > 0) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_string($value) || (is_object($value) && method_exists($value, '__toString'))) {
|
||||||
|
$str = (string)$value;
|
||||||
|
$int = Str\to_int($str);
|
||||||
|
if (null !== $int && $int > 0) {
|
||||||
|
return $int;
|
||||||
|
}
|
||||||
|
|
||||||
|
$trimmed = Str\trim_left($str, '0');
|
||||||
|
$int = Str\to_int($trimmed);
|
||||||
|
if (null !== $int && $int > 0) {
|
||||||
|
return $int;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exceptional case "000" -(trim)-> "", but we treat it as 0
|
||||||
|
if ('' === $trimmed && '' !== $str) {
|
||||||
|
CoercionException::withValue($value, $this->toString(), $this->getTrace());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_float($value)) {
|
||||||
|
$integer_value = (int) $value;
|
||||||
|
$reconstructed = (float) $integer_value;
|
||||||
|
if ($reconstructed === $value && $integer_value > 0) {
|
||||||
|
return $integer_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw CoercionException::withValue($value, $this->toString(), $this->getTrace());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
*
|
||||||
|
* @psalm-assert positive-int $value
|
||||||
|
*
|
||||||
|
* @throws AssertException
|
||||||
|
*
|
||||||
|
* @return positive-int
|
||||||
|
*/
|
||||||
|
public function assert($value): int
|
||||||
|
{
|
||||||
|
if (is_int($value) && $value > 0) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw AssertException::withValue($value, $this->toString(), $this->getTrace());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toString(): string
|
||||||
|
{
|
||||||
|
return 'positive-int';
|
||||||
|
}
|
||||||
|
}
|
13
src/Psl/Type/positive_int.php
Normal file
13
src/Psl/Type/positive_int.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Psl\Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return TypeInterface<positive-int>
|
||||||
|
*/
|
||||||
|
function positive_int(): TypeInterface
|
||||||
|
{
|
||||||
|
return new Internal\PositiveIntType();
|
||||||
|
}
|
69
tests/Psl/Type/PositiveIntTypeTest.php
Normal file
69
tests/Psl/Type/PositiveIntTypeTest.php
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Psl\Tests\Type;
|
||||||
|
|
||||||
|
use Psl\Math;
|
||||||
|
use Psl\Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends TypeTest<positive-int>
|
||||||
|
*/
|
||||||
|
final class PositiveIntTypeTest extends TypeTest
|
||||||
|
{
|
||||||
|
public function getType(): Type\TypeInterface
|
||||||
|
{
|
||||||
|
return Type\positive_int();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValidCoercions(): iterable
|
||||||
|
{
|
||||||
|
yield [123, 123];
|
||||||
|
yield ['123', 123];
|
||||||
|
yield [$this->stringable('123'), 123];
|
||||||
|
yield [$this->stringable((string) Math\INT16_MAX), Math\INT16_MAX];
|
||||||
|
yield [$this->stringable((string) Math\INT64_MAX), Math\INT64_MAX];
|
||||||
|
yield [(string) Math\INT64_MAX, Math\INT64_MAX];
|
||||||
|
yield [Math\INT64_MAX, Math\INT64_MAX];
|
||||||
|
yield ['7', 7];
|
||||||
|
yield ['07', 7];
|
||||||
|
yield ['007', 7];
|
||||||
|
yield [1.0, 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInvalidCoercions(): iterable
|
||||||
|
{
|
||||||
|
|
||||||
|
yield [0];
|
||||||
|
yield ['0'];
|
||||||
|
yield ['-321'];
|
||||||
|
yield [-321];
|
||||||
|
yield ['000'];
|
||||||
|
yield [1.23];
|
||||||
|
yield ['1.23'];
|
||||||
|
yield ['1e123'];
|
||||||
|
yield ['-1e123'];
|
||||||
|
yield [''];
|
||||||
|
yield [[]];
|
||||||
|
yield [[123]];
|
||||||
|
yield [null];
|
||||||
|
yield [false];
|
||||||
|
yield [$this->stringable('1.23')];
|
||||||
|
yield [$this->stringable('-007')];
|
||||||
|
yield [$this->stringable('-321')];
|
||||||
|
yield ['-007'];
|
||||||
|
yield ['9223372036854775808'];
|
||||||
|
yield [$this->stringable('9223372036854775808')];
|
||||||
|
yield ['-9223372036854775809'];
|
||||||
|
yield [$this->stringable('-9223372036854775809')];
|
||||||
|
yield ['0xFF'];
|
||||||
|
yield ['-0xFF'];
|
||||||
|
yield [''];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getToStringExamples(): iterable
|
||||||
|
{
|
||||||
|
yield [$this->getType(), 'positive-int'];
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user