-
-
Notifications
You must be signed in to change notification settings - Fork 75
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for value-of<BackedEnum>
- Loading branch information
Showing
8 changed files
with
235 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CuyZ\Valinor\Type\Parser\Exception\Enum; | ||
|
||
use CuyZ\Valinor\Type\Parser\Exception\InvalidType; | ||
use RuntimeException; | ||
|
||
/** @internal */ | ||
final class NotBackedEnum extends RuntimeException implements InvalidType | ||
{ | ||
public function __construct(string $enumName) | ||
{ | ||
parent::__construct( | ||
"`$enumName` is not BackedEnum.", | ||
1653468439 | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CuyZ\Valinor\Type\Parser\Exception\Magic; | ||
|
||
use CuyZ\Valinor\Type\Parser\Exception\InvalidType; | ||
use RuntimeException; | ||
|
||
/** @internal */ | ||
final class ClosingBracketMissing extends RuntimeException implements InvalidType | ||
{ | ||
public function __construct(string $symbol) | ||
{ | ||
parent::__construct( | ||
"The closing bracket is missing for `$symbol<...>`.", | ||
1618994728 | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CuyZ\Valinor\Type\Parser\Exception\Magic; | ||
|
||
use CuyZ\Valinor\Type\Parser\Exception\InvalidType; | ||
use CuyZ\Valinor\Type\Type; | ||
use RuntimeException; | ||
|
||
/** @internal */ | ||
final class NonArrayOf extends RuntimeException implements InvalidType | ||
{ | ||
public function __construct(string $symbol, Type $type) | ||
{ | ||
parent::__construct( | ||
"The type inside of `$symbol<{$type->toString()}>` is not an array.", | ||
1618994728 | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CuyZ\Valinor\Type\Parser\Exception\Magic; | ||
|
||
use CuyZ\Valinor\Type\Parser\Exception\InvalidType; | ||
use RuntimeException; | ||
|
||
/** @internal */ | ||
final class OpeningBracketMissing extends RuntimeException implements InvalidType | ||
{ | ||
public function __construct(string $symbol) | ||
{ | ||
parent::__construct( | ||
"The opening bracket is missing for `$symbol<...>`.", | ||
1618994728 | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CuyZ\Valinor\Type\Parser\Lexer\Token; | ||
|
||
use BackedEnum; | ||
use CuyZ\Valinor\Type\Parser\Exception\Enum\NotBackedEnum; | ||
use CuyZ\Valinor\Type\Parser\Exception\Magic\ClosingBracketMissing; | ||
use CuyZ\Valinor\Type\Parser\Exception\Magic\OpeningBracketMissing; | ||
use CuyZ\Valinor\Type\Parser\Lexer\TokenStream; | ||
use CuyZ\Valinor\Type\Type; | ||
use CuyZ\Valinor\Type\Types\EnumType; | ||
use CuyZ\Valinor\Type\Types\IntegerValueType; | ||
use CuyZ\Valinor\Type\Types\StringValueType; | ||
use CuyZ\Valinor\Type\Types\UnionType; | ||
use CuyZ\Valinor\Utility\IsSingleton; | ||
|
||
/** @internal */ | ||
final class ValueOfToken implements TraversingToken | ||
{ | ||
use IsSingleton; | ||
|
||
public function traverse(TokenStream $stream): Type | ||
{ | ||
if ($stream->done() || !$stream->forward() instanceof OpeningBracketToken) { | ||
throw new OpeningBracketMissing($this->symbol()); | ||
} | ||
|
||
$subType = $stream->read(); | ||
|
||
if ($stream->done() || !$stream->forward() instanceof ClosingBracketToken) { | ||
throw new ClosingBracketMissing($this->symbol()); | ||
} | ||
|
||
if ($subType instanceof UnionType && count($subType->types()) === 1) { | ||
Check warning on line 36 in src/Type/Parser/Lexer/Token/ValueOfToken.php GitHub Actions / Mutation tests
Check warning on line 36 in src/Type/Parser/Lexer/Token/ValueOfToken.php GitHub Actions / Mutation tests
Check warning on line 36 in src/Type/Parser/Lexer/Token/ValueOfToken.php GitHub Actions / Mutation tests
|
||
$subType = $subType->types()[0]; | ||
} | ||
|
||
if (! $subType instanceof EnumType) { | ||
Check warning on line 40 in src/Type/Parser/Lexer/Token/ValueOfToken.php GitHub Actions / Mutation tests
|
||
throw new NotBackedEnum($subType->toString()); | ||
} | ||
|
||
$list = []; | ||
foreach ($subType->cases() as $case) { | ||
if (! $case instanceof BackedEnum) { | ||
Check warning on line 46 in src/Type/Parser/Lexer/Token/ValueOfToken.php GitHub Actions / Mutation tests
|
||
throw new NotBackedEnum($this->symbol()); | ||
} | ||
if (is_string($case->value)) { | ||
$list[] = StringValueType::from("'$case->value'"); | ||
} else { | ||
$list[] = new IntegerValueType($case->value); | ||
} | ||
} | ||
|
||
return new UnionType(...$list); | ||
} | ||
|
||
public function symbol(): string | ||
{ | ||
return 'value-of'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace CuyZ\Valinor\Tests\Integration\Mapping; | ||
|
||
use CuyZ\Valinor\Mapper\MappingError; | ||
use CuyZ\Valinor\Mapper\Object\Constructor; | ||
use CuyZ\Valinor\Tests\Integration\IntegrationTestCase; | ||
|
||
final class EnumValueOfMappingTest extends IntegrationTestCase | ||
{ | ||
public function test_can_map_string_enum_value_of(): void | ||
{ | ||
try { | ||
$result = $this->mapperBuilder() | ||
->mapper() | ||
->map('value-of<' . SomeStringEnumForValueOf::class . '>', SomeStringEnumForValueOf::FOO->value); | ||
} catch (MappingError $error) { | ||
$this->mappingFail($error); | ||
} | ||
|
||
self::assertSame(SomeStringEnumForValueOf::FOO->value, $result); | ||
} | ||
|
||
public function test_can_map_integer_enum_value_of(): void | ||
{ | ||
try { | ||
$result = $this->mapperBuilder() | ||
->mapper() | ||
->map('value-of<' . SomeIntegerEnumForValueOf::class . '>', SomeIntegerEnumForValueOf::FOO->value); | ||
} catch (MappingError $error) { | ||
$this->mappingFail($error); | ||
} | ||
|
||
self::assertSame(SomeIntegerEnumForValueOf::FOO->value, $result); | ||
} | ||
|
||
public function test_array_keys_using_value_of(): void | ||
{ | ||
try { | ||
$result = $this->mapperBuilder() | ||
->mapper() | ||
->map('array<value-of<' . SomeStringEnumForValueOf::class . '>, string>', [SomeStringEnumForValueOf::FOO->value => 'foo']); | ||
} catch (MappingError $error) { | ||
$this->mappingFail($error); | ||
} | ||
|
||
self::assertSame([SomeStringEnumForValueOf::FOO->value => 'foo'], $result); | ||
} | ||
|
||
|
||
public function test_array_keys_using_value_of_error(): void | ||
{ | ||
try { | ||
$this->mapperBuilder() | ||
->mapper() | ||
->map('array<value-of<' . SomeStringEnumForValueOf::class . '>, string>', ['oof' => 'foo']); | ||
} catch (MappingError $exception) { | ||
$error = $exception->node()->children()['oof']->messages()[0]; | ||
self::assertSame("Key 'oof' does not match type `'FOO'|'FOZ'|'BAZ'`.", (string)$error); | ||
} | ||
} | ||
} | ||
|
||
enum SomeStringEnumForValueOf: string | ||
{ | ||
case FOO = 'FOO'; | ||
case FOZ = 'FOZ'; | ||
case BAZ = 'BAZ'; | ||
|
||
} | ||
enum SomeIntegerEnumForValueOf: int | ||
{ | ||
case FOO = 42; | ||
case FOZ = 404; | ||
case BAZ = 1337; | ||
|
||
} |