Skip to content

Commit 8e1afc3

Browse files
committed
Add non-zero-int, lowercase-string and uppercase-string types
1 parent aa87077 commit 8e1afc3

File tree

10 files changed

+154
-40
lines changed

10 files changed

+154
-40
lines changed

src/Platform/StandardPlatform.php

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,29 @@ public function getTypes(Direction $direction): iterable
4141

4242
// Adds support for the "string" type
4343
yield new Builder\SimpleTypeBuilder(['string', \Stringable::class], Type\StringType::class);
44+
if ($direction === Direction::Normalize) {
45+
yield new Builder\SimpleTypeBuilder('lowercase-string', Type\StringType::class);
46+
yield new Builder\SimpleTypeBuilder('uppercase-string', Type\StringType::class);
47+
} else {
48+
yield new Builder\SimpleTypeBuilder('lowercase-string', Type\LowercaseString::class);
49+
yield new Builder\SimpleTypeBuilder('uppercase-string', Type\UppercaseString::class);
50+
}
4451

4552
// Adds support for the "int" type
4653
yield new Builder\IntRangeTypeBuilder(['int', 'integer']);
47-
yield new Builder\PositiveIntBuilder('positive-int');
48-
yield new Builder\NonPositiveIntBuilder('non-positive-int');
49-
yield new Builder\NegativeIntBuilder('negative-int');
50-
yield new Builder\NonNegativeIntBuilder('non-negative-int');
51-
yield new Builder\NonZeroIntBuilder('non-zero-int');
54+
if ($direction === Direction::Normalize) {
55+
yield new Builder\SimpleTypeBuilder('positive-int', Type\IntType::class);
56+
yield new Builder\SimpleTypeBuilder('non-positive-int', Type\IntType::class);
57+
yield new Builder\SimpleTypeBuilder('negative-int', Type\IntType::class);
58+
yield new Builder\SimpleTypeBuilder('non-negative-int', Type\IntType::class);
59+
yield new Builder\SimpleTypeBuilder('non-zero-int', Type\IntType::class);
60+
} else {
61+
yield new Builder\PositiveIntBuilder('positive-int');
62+
yield new Builder\NonPositiveIntBuilder('non-positive-int');
63+
yield new Builder\NegativeIntBuilder('negative-int');
64+
yield new Builder\NonNegativeIntBuilder('non-negative-int');
65+
yield new Builder\SimpleTypeBuilder('non-zero-int', Type\NonZeroIntType::class);
66+
}
5267

5368
// Adds support for the "float" type
5469
yield new Builder\SimpleTypeBuilder(['float', 'double', 'real'], Type\FloatType::class);

src/Type/Builder/NegativeIntBuilder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
/**
1414
* @template-extends NamedTypeBuilder<IntRangeType>
1515
*/
16-
final class NegativeIntBuilder extends NamedTypeBuilder
16+
class NegativeIntBuilder extends NamedTypeBuilder
1717
{
1818
public function build(
1919
TypeStatement $statement,

src/Type/Builder/NonNegativeIntBuilder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
/**
1414
* @template-extends NamedTypeBuilder<IntRangeType>
1515
*/
16-
final class NonNegativeIntBuilder extends NamedTypeBuilder
16+
class NonNegativeIntBuilder extends NamedTypeBuilder
1717
{
1818
public function build(
1919
TypeStatement $statement,

src/Type/Builder/NonPositiveIntBuilder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
/**
1414
* @template-extends NamedTypeBuilder<IntRangeType>
1515
*/
16-
final class NonPositiveIntBuilder extends NamedTypeBuilder
16+
class NonPositiveIntBuilder extends NamedTypeBuilder
1717
{
1818
public function build(
1919
TypeStatement $statement,

src/Type/Builder/NonZeroIntBuilder.php

Lines changed: 0 additions & 31 deletions
This file was deleted.

src/Type/Builder/PositiveIntBuilder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
/**
1414
* @template-extends NamedTypeBuilder<IntRangeType>
1515
*/
16-
final class PositiveIntBuilder extends NamedTypeBuilder
16+
class PositiveIntBuilder extends NamedTypeBuilder
1717
{
1818
public function build(
1919
TypeStatement $statement,

src/Type/LowercaseString.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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\StringTypeCoercer;
10+
use TypeLang\Mapper\Type\Coercer\TypeCoercerInterface;
11+
12+
/**
13+
* @template-implements TypeInterface<non-empty-string>
14+
*/
15+
class LowercaseString 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) && $this->isLowerString($value);
30+
}
31+
32+
private function isLowerString(string $value): bool
33+
{
34+
if ($value === '') {
35+
return true;
36+
}
37+
38+
if (\function_exists('\\ctype_lower')) {
39+
return \ctype_lower($value);
40+
}
41+
42+
return !\preg_match('/[^a-z]/', $value);
43+
}
44+
45+
public function cast(mixed $value, Context $context): string
46+
{
47+
$coerced = $value;
48+
49+
if (!\is_string($value) && !$context->isStrictTypesEnabled()) {
50+
$coerced = $this->coercer->coerce($value, $context);
51+
}
52+
53+
if ($this->match($coerced, $context)) {
54+
return $coerced;
55+
}
56+
57+
throw InvalidValueException::createFromContext(
58+
value: $value,
59+
context: $context,
60+
);
61+
}
62+
}

src/Type/UppercaseString.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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\StringTypeCoercer;
10+
use TypeLang\Mapper\Type\Coercer\TypeCoercerInterface;
11+
12+
/**
13+
* @template-implements TypeInterface<non-empty-string>
14+
*/
15+
class UppercaseString 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) && $this->isUpperString($value);
30+
}
31+
32+
private function isUpperString(string $value): bool
33+
{
34+
if ($value === '') {
35+
return true;
36+
}
37+
38+
if (\function_exists('\\ctype_upper')) {
39+
return \ctype_upper($value);
40+
}
41+
42+
return !\preg_match('/[^A-Z]/', $value);
43+
}
44+
45+
public function cast(mixed $value, Context $context): string
46+
{
47+
$coerced = $value;
48+
49+
if (!\is_string($value) && !$context->isStrictTypesEnabled()) {
50+
$coerced = $this->coercer->coerce($value, $context);
51+
}
52+
53+
if ($this->match($coerced, $context)) {
54+
return $coerced;
55+
}
56+
57+
throw InvalidValueException::createFromContext(
58+
value: $value,
59+
context: $context,
60+
);
61+
}
62+
}

tests/Platform/PlatformTestCase.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
abstract class PlatformTestCase extends TestCase
2222
{
23+
// v2.1
2324
protected const TYPES_PHPSTAN = [
2425
'int',
2526
'integer',
@@ -30,6 +31,7 @@ abstract class PlatformTestCase extends TestCase
3031
'non-zero-int',
3132
'string',
3233
'lowercase-string',
34+
'uppercase-string',
3335
'literal-string',
3436
'class-string',
3537
'interface-string',
@@ -45,6 +47,7 @@ abstract class PlatformTestCase extends TestCase
4547
'numeric-string',
4648
'non-empty-string',
4749
'non-empty-lowercase-string',
50+
'non-empty-uppercase-string',
4851
'truthy-string',
4952
'non-falsy-string',
5053
'non-empty-literal-string',

tests/Platform/StandardPlatformTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ final class StandardPlatformTest extends PlatformTestCase
4646
'non-positive-int',
4747
'negative-int',
4848
'non-negative-int',
49+
'non-zero-int',
50+
'lowercase-string',
51+
'uppercase-string',
4952
// composite
5053
'?int',
5154
'int|false',

0 commit comments

Comments
 (0)