Skip to content

Commit 66b6088

Browse files
committed
Add non-empty-string and numeric-string
1 parent 8e7e912 commit 66b6088

14 files changed

+246
-38
lines changed

src/Platform/StandardPlatform.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,22 @@ public function getTypes(Direction $direction): iterable
7676
// Adds support for the float literal types
7777
yield new Builder\FloatLiteralTypeBuilder();
7878

79+
// Adds support for the string literal types
80+
yield new Builder\StringLiteralTypeBuilder();
81+
7982
// Adds support for the "T[]" statement
8083
yield new Builder\TypesListBuilder();
8184

8285
// Adds support for the "T|U" union types
8386
yield new Builder\UnionTypeBuilder();
8487

8588
if ($direction === Direction::Normalize) {
89+
// Adds support for "non-empty-string", "numeric-string" which
90+
// are similar to simple string
91+
yield new Builder\SimpleTypeBuilder(
92+
names: ['non-empty-string', 'numeric-string'],
93+
type: Type\StringType::class,
94+
);
8695
// Adds support for the "iterable<T> -> list<T>" type
8796
yield new Builder\ListFromIterableTypeBuilder('list', 'mixed');
8897
// Adds support for the "object -> array{ ... }" type
@@ -96,6 +105,10 @@ public function getTypes(Direction $direction): iterable
96105
// Adds support for the "object(ClassName) -> array{ ... }" type
97106
yield new Builder\ClassToArrayTypeBuilder($this->meta);
98107
} else {
108+
// Adds support for "non-empty-string"
109+
yield new Builder\SimpleTypeBuilder('non-empty-string', Type\NonEmptyString::class);
110+
// Adds support for "numeric-string"
111+
yield new Builder\SimpleTypeBuilder('numeric-string', Type\NumericString::class);
99112
// Adds support for the "array<T> -> list<T>" type
100113
yield new Builder\ListFromArrayTypeBuilder('list', 'mixed');
101114
// Adds support for the "array{ ... } -> object" type

src/Type/ArrayKeyType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function __construct(
3030
) {}
3131

3232
/**
33-
* @phpstan-assert-if-true string|int $value
33+
* @phpstan-assert-if-true array-key $value
3434
*/
3535
public function match(mixed $value, Context $context): bool
3636
{

src/Type/BoolLiteralType.php

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,27 @@
99
use TypeLang\Mapper\Type\Coercer\BoolTypeCoercer;
1010
use TypeLang\Mapper\Type\Coercer\TypeCoercerInterface;
1111

12-
class BoolLiteralType extends BoolType
12+
/**
13+
* @template-implements TypeInterface<bool>
14+
*/
15+
class BoolLiteralType implements TypeInterface
1316
{
14-
/**
15-
* @param TypeCoercerInterface<bool> $coercer
16-
*/
1717
public function __construct(
1818
protected readonly bool $value,
19-
TypeCoercerInterface $coercer = new BoolTypeCoercer(),
20-
) {
21-
parent::__construct($coercer);
22-
}
19+
/**
20+
* @var TypeCoercerInterface<bool>
21+
*/
22+
protected readonly TypeCoercerInterface $coercer = new BoolTypeCoercer(),
23+
) {}
2324

24-
#[\Override]
25+
/**
26+
* @phpstan-assert-if-true bool $value
27+
*/
2528
public function match(mixed $value, Context $context): bool
2629
{
2730
return $value === $this->value;
2831
}
2932

30-
#[\Override]
3133
public function cast(mixed $value, Context $context): bool
3234
{
3335
// Fast return in case of value if not castable

src/Type/BoolType.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public function __construct(
2121
protected readonly TypeCoercerInterface $coercer = new BoolTypeCoercer(),
2222
) {}
2323

24+
/**
25+
* @phpstan-assert-if-true bool $value
26+
*/
2427
public function match(mixed $value, Context $context): bool
2528
{
2629
return \is_bool($value);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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\StringLiteralType;
10+
use TypeLang\Parser\Node\Literal\StringLiteralNode;
11+
use TypeLang\Parser\Node\Stmt\TypeStatement;
12+
13+
/**
14+
* @template-implements TypeBuilderInterface<StringLiteralNode, StringLiteralType>
15+
*/
16+
class StringLiteralTypeBuilder implements TypeBuilderInterface
17+
{
18+
public function isSupported(TypeStatement $statement): bool
19+
{
20+
return $statement instanceof StringLiteralNode;
21+
}
22+
23+
public function build(
24+
TypeStatement $statement,
25+
TypeRepositoryInterface $types,
26+
TypeParserInterface $parser,
27+
): StringLiteralType {
28+
return new StringLiteralType($statement->value);
29+
}
30+
}

src/Type/FloatLiteralType.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,21 @@
99
use TypeLang\Mapper\Type\Coercer\FloatTypeCoercer;
1010
use TypeLang\Mapper\Type\Coercer\TypeCoercerInterface;
1111

12-
class FloatLiteralType extends FloatType
12+
/**
13+
* @template-implements TypeInterface<float>
14+
*/
15+
class FloatLiteralType implements TypeInterface
1316
{
1417
private readonly float $value;
1518

16-
/**
17-
* @param TypeCoercerInterface<float> $coercer
18-
*/
1919
public function __construct(
2020
int|float $value,
21-
TypeCoercerInterface $coercer = new FloatTypeCoercer(),
21+
/**
22+
* @var TypeCoercerInterface<float>
23+
*/
24+
protected readonly TypeCoercerInterface $coercer = new FloatTypeCoercer(),
2225
) {
2326
$this->value = (float) $value;
24-
25-
parent::__construct($coercer);
2627
}
2728

2829
/**

src/Type/IntLiteralType.php

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,22 @@
99
use TypeLang\Mapper\Type\Coercer\IntTypeCoercer;
1010
use TypeLang\Mapper\Type\Coercer\TypeCoercerInterface;
1111

12-
class IntLiteralType extends IntType
12+
/**
13+
* @template-implements TypeInterface<int>
14+
*/
15+
class IntLiteralType implements TypeInterface
1316
{
14-
/**
15-
* @param TypeCoercerInterface<int> $coercer
16-
*/
1717
public function __construct(
1818
protected readonly int $value,
19-
TypeCoercerInterface $coercer = new IntTypeCoercer(),
20-
) {
21-
parent::__construct($coercer);
22-
}
19+
/**
20+
* @var TypeCoercerInterface<int>
21+
*/
22+
protected readonly TypeCoercerInterface $coercer = new IntTypeCoercer(),
23+
) {}
2324

25+
/**
26+
* @phpstan-assert-if-true int $value
27+
*/
2428
public function match(mixed $value, Context $context): bool
2529
{
2630
return $value === $this->value;

src/Type/IntRangeType.php

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,33 @@
99
use TypeLang\Mapper\Type\Coercer\IntTypeCoercer;
1010
use TypeLang\Mapper\Type\Coercer\TypeCoercerInterface;
1111

12-
class IntRangeType extends IntType
12+
/**
13+
* @template-implements TypeInterface<int>
14+
*/
15+
class IntRangeType implements TypeInterface
1316
{
1417
public const DEFAULT_INT_MIN = \PHP_INT_MIN;
1518
public const DEFAULT_INT_MAX = \PHP_INT_MAX;
1619

17-
/**
18-
* @param TypeCoercerInterface<int> $coercer
19-
*/
2020
public function __construct(
2121
protected readonly int $min = self::DEFAULT_INT_MIN,
2222
protected readonly int $max = self::DEFAULT_INT_MAX,
23-
TypeCoercerInterface $coercer = new IntTypeCoercer(),
24-
) {
25-
parent::__construct($coercer);
26-
}
23+
/**
24+
* @var TypeCoercerInterface<int>
25+
*/
26+
protected readonly TypeCoercerInterface $coercer = new IntTypeCoercer(),
27+
) {}
2728

2829
/**
2930
* @phpstan-assert-if-true int $value
3031
*/
31-
#[\Override]
3232
public function match(mixed $value, Context $context): bool
3333
{
3434
return \is_int($value)
3535
&& $value >= $this->min
3636
&& $value <= $this->max;
3737
}
3838

39-
#[\Override]
4039
public function cast(mixed $value, Context $context): int
4140
{
4241
$coerced = $value;

src/Type/IntType.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public function __construct(
2121
protected readonly TypeCoercerInterface $coercer = new IntTypeCoercer(),
2222
) {}
2323

24+
/**
25+
* @phpstan-assert-if-true int $value
26+
*/
2427
public function match(mixed $value, Context $context): bool
2528
{
2629
return \is_int($value);

src/Type/NonEmptyString.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\Exception\Mapping\InvalidValueException;
8+
use TypeLang\Mapper\Runtime\Context;
9+
use TypeLang\Mapper\Type\Coercer\StringTypeCoercer;
10+
use TypeLang\Mapper\Type\Coercer\TypeCoercerInterface;
11+
12+
/**
13+
* @template-implements TypeInterface<non-empty-string>
14+
*/
15+
class NonEmptyString implements TypeInterface
16+
{
17+
public function __construct(
18+
/**
19+
* @var TypeCoercerInterface<string>
20+
*/
21+
protected readonly TypeCoercerInterface $coercer = new StringTypeCoercer(),
22+
) {}
23+
24+
/**
25+
* @phpstan-assert-if-true non-empty-string $value
26+
*/
27+
public function match(mixed $value, Context $context): bool
28+
{
29+
return \is_string($value) && $value !== '';
30+
}
31+
32+
public function cast(mixed $value, Context $context): string
33+
{
34+
$coerced = $value;
35+
36+
if (!\is_string($value) && !$context->isStrictTypesEnabled()) {
37+
$coerced = $this->coercer->coerce($value, $context);
38+
}
39+
40+
if ($this->match($coerced, $context)) {
41+
return $coerced;
42+
}
43+
44+
throw InvalidValueException::createFromContext(
45+
value: $value,
46+
context: $context,
47+
);
48+
}
49+
}

0 commit comments

Comments
 (0)