Skip to content

Commit aa87077

Browse files
committed
Add non-zero-int type
1 parent a54d37b commit aa87077

File tree

4 files changed

+136
-0
lines changed

4 files changed

+136
-0
lines changed

src/Platform/StandardPlatform.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public function getTypes(Direction $direction): iterable
4848
yield new Builder\NonPositiveIntBuilder('non-positive-int');
4949
yield new Builder\NegativeIntBuilder('negative-int');
5050
yield new Builder\NonNegativeIntBuilder('non-negative-int');
51+
yield new Builder\NonZeroIntBuilder('non-zero-int');
5152

5253
// Adds support for the "float" type
5354
yield new Builder\SimpleTypeBuilder(['float', 'double', 'real'], Type\FloatType::class);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypeLang\Mapper\Type\Builder;
6+
7+
use TypeLang\Mapper\Runtime\Parser\TypeParserInterface;
8+
use TypeLang\Mapper\Runtime\Repository\TypeRepositoryInterface;
9+
use TypeLang\Mapper\Type\NonZeroIntType;
10+
use TypeLang\Parser\Node\Stmt\NamedTypeNode;
11+
use TypeLang\Parser\Node\Stmt\TypeStatement;
12+
13+
/**
14+
* @template-extends NamedTypeBuilder<NonZeroIntType>
15+
*/
16+
final class NonZeroIntBuilder extends NamedTypeBuilder
17+
{
18+
public function build(
19+
TypeStatement $statement,
20+
TypeRepositoryInterface $types,
21+
TypeParserInterface $parser,
22+
): NonZeroIntType {
23+
/** @phpstan-ignore-next-line : Additional DbC assertion */
24+
assert($statement instanceof NamedTypeNode);
25+
26+
$this->expectNoShapeFields($statement);
27+
$this->expectNoTemplateArguments($statement);
28+
29+
return new NonZeroIntType();
30+
}
31+
}

src/Type/NonZeroIntType.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypeLang\Mapper\Type;
6+
7+
use TypeLang\Mapper\Context\Context;
8+
use TypeLang\Mapper\Exception\Mapping\InvalidValueException;
9+
use TypeLang\Mapper\Type\Coercer\IntTypeCoercer;
10+
use TypeLang\Mapper\Type\Coercer\TypeCoercerInterface;
11+
12+
/**
13+
* @template-implements TypeInterface<int>
14+
*/
15+
class NonZeroIntType implements TypeInterface
16+
{
17+
public function __construct(
18+
/**
19+
* @var TypeCoercerInterface<int>
20+
*/
21+
protected readonly TypeCoercerInterface $coercer = new IntTypeCoercer(),
22+
) {}
23+
24+
/**
25+
* @phpstan-assert-if-true int $value
26+
*/
27+
public function match(mixed $value, Context $context): bool
28+
{
29+
return \is_int($value) && $value !== 0;
30+
}
31+
32+
public function cast(mixed $value, Context $context): int
33+
{
34+
$coerced = $value;
35+
36+
if (!\is_int($value) && !$context->isStrictTypesEnabled()) {
37+
$coerced = $this->coercer->coerce($value, $context);
38+
}
39+
40+
if (\is_int($value) && $value !== 0) {
41+
return $coerced;
42+
}
43+
44+
throw InvalidValueException::createFromContext(
45+
value: $value,
46+
context: $context,
47+
);
48+
}
49+
}

tests/Type/NonZeroIntTypeTest.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypeLang\Mapper\Tests\Type;
6+
7+
use PHPUnit\Framework\Attributes\CoversClass;
8+
use PHPUnit\Framework\Attributes\Group;
9+
use TypeLang\Mapper\Type\Coercer\TypeCoercerInterface;
10+
use TypeLang\Mapper\Type\NonZeroIntType;
11+
use TypeLang\Mapper\Type\TypeInterface;
12+
13+
#[Group('type')]
14+
#[CoversClass(NonZeroIntType::class)]
15+
final class NonZeroIntTypeTest extends CoercibleTypeTestCase
16+
{
17+
protected static function createType(?TypeCoercerInterface $coercer = null): TypeInterface
18+
{
19+
if ($coercer !== null) {
20+
return new NonZeroIntType(coercer: $coercer);
21+
}
22+
23+
return new NonZeroIntType();
24+
}
25+
26+
protected static function matchValues(bool $normalize): iterable
27+
{
28+
foreach (self::defaultMatchDataProviderSamples() as $value => $default) {
29+
yield $value => match (true) {
30+
$value === \PHP_INT_MAX,
31+
$value === \PHP_INT_MIN,
32+
$value === 42,
33+
$value === -42,
34+
$value === 1,
35+
$value === -1 => true,
36+
default => $default,
37+
};
38+
}
39+
}
40+
41+
protected static function castValues(bool $normalize): iterable
42+
{
43+
foreach (self::defaultCastDataProviderSamples() as $value => $default) {
44+
yield $value => match (true) {
45+
$value === \PHP_INT_MAX => \PHP_INT_MAX,
46+
$value === \PHP_INT_MIN => \PHP_INT_MIN,
47+
$value === 42 => 42,
48+
$value === -42 => -42,
49+
$value === 1 => 1,
50+
$value === -1 => -1,
51+
default => $default,
52+
};
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)