Skip to content

Commit

Permalink
feat(type): add unsigned integer types (#395)
Browse files Browse the repository at this point in the history
  • Loading branch information
KennedyTedesco committed Jan 19, 2023
1 parent f53a5a5 commit 8dabaa0
Show file tree
Hide file tree
Showing 11 changed files with 491 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/component/type.md
Expand Up @@ -47,6 +47,9 @@
- [scalar](./../../src/Psl/Type/scalar.php#L10)
- [shape](./../../src/Psl/Type/shape.php#L15)
- [string](./../../src/Psl/Type/string.php#L10)
- [u16](./../../src/Psl/Type/u16.php#L12)
- [u32](./../../src/Psl/Type/u32.php#L12)
- [u8](./../../src/Psl/Type/u8.php#L12)
- [uint](./../../src/Psl/Type/uint.php#L12)
- [union](./../../src/Psl/Type/union.php#L16)
- [unit_enum](./../../src/Psl/Type/unit_enum.php#L16)
Expand Down
6 changes: 6 additions & 0 deletions src/Psl/Internal/Loader.php
Expand Up @@ -331,6 +331,9 @@ final class Loader
'Psl\\Type\\scalar' => 'Psl/Type/scalar.php',
'Psl\\Type\\shape' => 'Psl/Type/shape.php',
'Psl\\Type\\uint' => 'Psl/Type/uint.php',
'Psl\\Type\\u32' => 'Psl/Type/u32.php',
'Psl\\Type\\u16' => 'Psl/Type/u16.php',
'Psl\\Type\\u8' => 'Psl/Type/u8.php',
'Psl\\Type\\i8' => 'Psl/Type/i8.php',
'Psl\\Type\\i16' => 'Psl/Type/i16.php',
'Psl\\Type\\i32' => 'Psl/Type/i32.php',
Expand Down Expand Up @@ -646,6 +649,9 @@ final class Loader
'Psl\\Type\\Internal\\I16Type' => 'Psl/Type/Internal/I16Type.php',
'Psl\\Type\\Internal\\I32Type' => 'Psl/Type/Internal/I32Type.php',
'Psl\\Type\\Internal\\I64Type' => 'Psl/Type/Internal/I64Type.php',
'Psl\\Type\\Internal\\U8Type' => 'Psl/Type/Internal/U8Type.php',
'Psl\\Type\\Internal\\U16Type' => 'Psl/Type/Internal/U16Type.php',
'Psl\\Type\\Internal\\U32Type' => 'Psl/Type/Internal/U32Type.php',
'Psl\\Type\\Internal\\UnionType' => 'Psl/Type/Internal/UnionType.php',
'Psl\\Type\\Internal\\VecType' => 'Psl/Type/Internal/VecType.php',
'Psl\\Type\\Internal\\DictType' => 'Psl/Type/Internal/DictType.php',
Expand Down
78 changes: 78 additions & 0 deletions src/Psl/Type/Internal/U16Type.php
@@ -0,0 +1,78 @@
<?php

declare(strict_types=1);

namespace Psl\Type\Internal;

use Psl\Math;
use Psl\Type;
use Psl\Type\Exception\AssertException;
use Psl\Type\Exception\CoercionException;

use function is_int;
use function Psl\Type;

/**
* @ara-extends Type\Type<u16>
*
* @extends Type\Type<int<0, 65535>>
*
* @internal
*/
final class U16Type extends Type\Type
{
/**
* @ara-assert-if-true u16 $value
*
* @psalm-assert-if-true int<0, 65535> $value
*/
public function matches(mixed $value): bool
{
return is_int($value) && $value >= 0 && $value <= MATH\UINT16_MAX;
}

/**
* @throws CoercionException
*
* @ara-return u16
*
* @return int<0, 65535>
*/
public function coerce(mixed $value): int
{
$integer = Type\int()
->withTrace($this->getTrace()->withFrame($this->toString()))
->coerce($value);

if ($integer >= 0 && $integer <= MATH\UINT16_MAX) {
return $integer;
}

throw CoercionException::withValue($value, $this->toString(), $this->getTrace());
}

/**
* @ara-assert u16 $value
*
* @psalm-assert int<0, 65535> $value
*
* @throws AssertException
*
* @ara-return u16
*
* @return int<0, 65535>
*/
public function assert(mixed $value): int
{
if (is_int($value) && $value >= 0 && $value <= MATH\UINT16_MAX) {
return $value;
}

throw AssertException::withValue($value, $this->toString(), $this->getTrace());
}

public function toString(): string
{
return 'u16';
}
}
78 changes: 78 additions & 0 deletions src/Psl/Type/Internal/U32Type.php
@@ -0,0 +1,78 @@
<?php

declare(strict_types=1);

namespace Psl\Type\Internal;

use Psl\Math;
use Psl\Type;
use Psl\Type\Exception\AssertException;
use Psl\Type\Exception\CoercionException;

use function is_int;
use function Psl\Type;

/**
* @ara-extends Type\Type<u32>
*
* @extends Type\Type<int<0, 4294967295>>
*
* @internal
*/
final class U32Type extends Type\Type
{
/**
* @ara-assert-if-true u32 $value
*
* @psalm-assert-if-true int<0, 4294967295> $value
*/
public function matches(mixed $value): bool
{
return is_int($value) && $value >= 0 && $value <= MATH\UINT32_MAX;
}

/**
* @throws CoercionException
*
* @ara-return u32
*
* @return int<0, 4294967295>
*/
public function coerce(mixed $value): int
{
$integer = Type\int()
->withTrace($this->getTrace()->withFrame($this->toString()))
->coerce($value);

if ($integer >= 0 && $integer <= MATH\UINT32_MAX) {
return $integer;
}

throw CoercionException::withValue($value, $this->toString(), $this->getTrace());
}

/**
* @ara-assert u32 $value
*
* @psalm-assert int<0, 4294967295> $value
*
* @throws AssertException
*
* @ara-return u32
*
* @return int<0, 4294967295>
*/
public function assert(mixed $value): int
{
if (is_int($value) && $value >= 0 && $value <= MATH\UINT32_MAX) {
return $value;
}

throw AssertException::withValue($value, $this->toString(), $this->getTrace());
}

public function toString(): string
{
return 'u32';
}
}
78 changes: 78 additions & 0 deletions src/Psl/Type/Internal/U8Type.php
@@ -0,0 +1,78 @@
<?php

declare(strict_types=1);

namespace Psl\Type\Internal;

use Psl\Math;
use Psl\Type;
use Psl\Type\Exception\AssertException;
use Psl\Type\Exception\CoercionException;

use function is_int;
use function Psl\Type;

/**
* @ara-extends Type\Type<u8>
*
* @extends Type\Type<int<0, 255>>
*
* @internal
*/
final class U8Type extends Type\Type
{
/**
* @ara-assert-if-true u8 $value
*
* @psalm-assert-if-true int<0, 255> $value
*/
public function matches(mixed $value): bool
{
return is_int($value) && $value >= 0 && $value <= MATH\UINT8_MAX;
}

/**
* @throws CoercionException
*
* @ara-return u8
*
* @return int<0, 255>
*/
public function coerce(mixed $value): int
{
$integer = Type\int()
->withTrace($this->getTrace()->withFrame($this->toString()))
->coerce($value);

if ($integer >= 0 && $integer <= MATH\UINT8_MAX) {
return $integer;
}

throw CoercionException::withValue($value, $this->toString(), $this->getTrace());
}

/**
* @ara-assert u8 $value
*
* @psalm-assert int<0, 255> $value
*
* @throws AssertException
*
* @ara-return u8
*
* @return int<0, 255>
*/
public function assert(mixed $value): int
{
if (is_int($value) && $value >= 0 && $value <= MATH\UINT8_MAX) {
return $value;
}

throw AssertException::withValue($value, $this->toString(), $this->getTrace());
}

public function toString(): string
{
return 'u8';
}
}
15 changes: 15 additions & 0 deletions src/Psl/Type/u16.php
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Psl\Type;

/**
* @ara-return TypeInterface<u16>
*
* @return TypeInterface<int<0, 65535>>
*/
function u16(): TypeInterface
{
return new Internal\U16Type();
}
15 changes: 15 additions & 0 deletions src/Psl/Type/u32.php
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Psl\Type;

/**
* @ara-return TypeInterface<u32>
*
* @return TypeInterface<int<0, 4294967295>>
*/
function u32(): TypeInterface
{
return new Internal\U32Type();
}
15 changes: 15 additions & 0 deletions src/Psl/Type/u8.php
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Psl\Type;

/**
* @ara-return TypeInterface<u8>
*
* @return TypeInterface<int<0, 255>>
*/
function u8(): TypeInterface
{
return new Internal\U8Type();
}
67 changes: 67 additions & 0 deletions tests/unit/Type/U16TypeTest.php
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

namespace Psl\Tests\Unit\Type;

use Psl\Math;
use Psl\Type;

final class U16TypeTest extends TypeTest
{
public function getType(): Type\TypeInterface
{
return Type\u16();
}

public function getValidCoercions(): iterable
{
yield [65535, 65535];
yield [0, 0];
yield ['0', 0];
yield ['123', 123];
yield [$this->stringable('123'), 123];
yield ['7', 7];
yield ['07', 7];
yield ['007', 7];
yield ['000', 0];
yield [1.0, 1];
yield [$this->stringable((string) Math\INT16_MAX), Math\INT16_MAX];
yield [$this->stringable((string) Math\INT8_MAX), Math\INT8_MAX];
yield [$this->stringable((string) Math\UINT16_MAX), Math\UINT16_MAX];
yield [$this->stringable((string) Math\UINT8_MAX), Math\UINT8_MAX];
}

public function getInvalidCoercions(): iterable
{
yield [-123];
yield [1.23];
yield ['1.23'];
yield ['1e123'];
yield [''];
yield [[]];
yield [[123]];
yield [null];
yield [false];
yield [$this->stringable('1.23')];
yield [$this->stringable('-007')];
yield ['-007'];
yield ['4294967296'];
yield [$this->stringable('4294967296')];
yield ['-4294967295'];
yield ['0xFF'];
yield [''];
yield [$this->stringable((string) Math\INT8_MIN)];
yield [$this->stringable((string) Math\INT16_MIN)];
yield [$this->stringable((string) Math\INT32_MIN)];
yield [$this->stringable((string) Math\INT32_MAX)];
yield [$this->stringable((string) Math\INT64_MAX)];
yield [(string) Math\INT64_MAX];
yield [Math\INT64_MAX];
}

public function getToStringExamples(): iterable
{
yield [$this->getType(), 'u16'];
}
}

0 comments on commit 8dabaa0

Please sign in to comment.