Skip to content

Commit

Permalink
Better convert coerce exception
Browse files Browse the repository at this point in the history
  • Loading branch information
veewee committed Mar 29, 2024
1 parent 1afc349 commit 2c5fcd7
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 32 deletions.
15 changes: 15 additions & 0 deletions src/Psl/Type/Exception/PathExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,19 @@ public static function iteratorError(mixed $previousKey): string
{
return self::expression($previousKey === null ? 'first()' : '%s.next()', $previousKey);
}

public static function coerceInput(mixed $input, string $expectedType): string
{
return Str\format('coerce_input(%s): %s', get_debug_type($input), $expectedType);
}

public static function convert(mixed $input, string $expectedType): string
{
return Str\format('convert(%s): %s', get_debug_type($input), $expectedType);
}

public static function coerceOutput(mixed $input, string $expectedType): string
{
return Str\format('coerce_output(%s): %s', get_debug_type($input), $expectedType);
}
}
23 changes: 19 additions & 4 deletions src/Psl/Type/Internal/ConvertedType.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Psl\Type;
use Psl\Type\Exception\AssertException;
use Psl\Type\Exception\CoercionException;
use Psl\Type\Exception\PathExpression;
use Psl\Type\TypeInterface;
use Throwable;

Expand Down Expand Up @@ -48,15 +49,29 @@ public function coerce(mixed $value): mixed
return $value;
}

$coercedInput = $this->from->coerce($value);
$action = 0;

try {
$coercedInput = $this->from->coerce($value);
$action++;
$converted = ($this->converter)($coercedInput);
$action++;
return $this->into->coerce($converted);
} catch (Throwable $failure) {
throw CoercionException::withValue($value, $this->toString(), previous: $failure);
throw CoercionException::withValue(
$value,
match ($action) {
0 => $this->from->toString(),
default => $this->into->toString(),
},
match ($action) {
0 => PathExpression::coerceInput($value, $this->from->toString()),
1 => PathExpression::convert($coercedInput ?? null, $this->into->toString()),
default => PathExpression::coerceOutput($converted ?? null, $this->into->toString()),
},
$failure
);
}

return $this->into->coerce($converted);
}

/**
Expand Down
47 changes: 47 additions & 0 deletions tests/unit/Type/ConvertedTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Psl\Tests\Unit\Type;

use DateTimeImmutable;
use Psl\Str;
use Psl\Type;
use RuntimeException;

Expand Down Expand Up @@ -61,4 +62,50 @@ public function getToStringExamples(): iterable
{
yield [$this->getType(), DateTimeImmutable::class];
}

public static function provideCoerceExceptionExpectations(): iterable
{
yield 'Coerce input error' => [
Type\converted(
Type\int(),
Type\string(),
static fn (int $i): string => (string) $i
),
new class () {
},
'Could not coerce "class@anonymous" to type "int" at path "coerce_input(class@anonymous): int".'
];
yield 'Convert exception error' => [
Type\converted(
Type\int(),
Type\string(),
static fn (int $i): string => throw new RuntimeException('not possible')
),
1,
'Could not coerce "int" to type "string" at path "convert(int): string": not possible.'
];
yield 'Coerce output error' => [
Type\converted(
Type\int(),
Type\string(),
static fn (int $i): object => new class () {
}
),
1,
'Could not coerce "class@anonymous" to type "string" at path "coerce_output(class@anonymous): string".'
];
}

/**
* @dataProvider provideCoerceExceptionExpectations
*/
public function testInvalidCoercionTypeExceptions(Type\TypeInterface $type, mixed $data, string $expectedMessage): void
{
try {
$type->coerce($data);
static::fail(Str\format('Expected "%s" exception to be thrown.', Type\Exception\CoercionException::class));
} catch (Type\Exception\CoercionException $e) {
static::assertSame($expectedMessage, $e->getMessage());
}
}
}
18 changes: 18 additions & 0 deletions tests/unit/Type/Exception/PathExpressionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,22 @@ public function testIteratorError(): void
static::assertSame('first()', PathExpression::iteratorError(null));
static::assertSame('foo.next()', PathExpression::iteratorError('foo'));
}

public function testCoerceInput(): void
{
static::assertSame('coerce_input(string): string', PathExpression::coerceInput('foo', 'string'));
static::assertSame('coerce_input(array): string', PathExpression::coerceInput([], 'string'));
}

public function testConvert(): void
{
static::assertSame('convert(string): string', PathExpression::convert('foo', 'string'));
static::assertSame('convert(array): string', PathExpression::convert([], 'string'));
}

public function testCoerceOutput(): void
{
static::assertSame('coerce_output(string): string', PathExpression::coerceOutput('foo', 'string'));
static::assertSame('coerce_output(array): string', PathExpression::coerceOutput([], 'string'));
}
}
28 changes: 0 additions & 28 deletions tests/unit/Type/Exception/TypeCoercionExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Psl\Collection;
use Psl\Str;
use Psl\Type;
use RuntimeException;

final class TypeCoercionExceptionTest extends TestCase
{
Expand Down Expand Up @@ -57,33 +56,6 @@ public function testIncorrectResourceType(): void
}
}

public function testConversionFailure(): void
{
$type = Type\converted(
Type\int(),
Type\string(),
static fn (int $i): string => throw new RuntimeException('not possible')
);

try {
$type->coerce(1);

static::fail(Str\format(
'Expected "%s" exception to be thrown.',
Type\Exception\CoercionException::class
));
} catch (Type\Exception\CoercionException $e) {
static::assertSame('string', $e->getTargetType());
static::assertSame('int', $e->getActualType());
static::assertSame(0, $e->getCode());
static::assertSame(Str\format(
'Could not coerce "int" to type "string": not possible.',
Collection\Map::class
), $e->getMessage());
static::assertSame([], $e->getPaths());
}
}

public function testIncorrectNestedType()
{
$type = Type\shape([
Expand Down

0 comments on commit 2c5fcd7

Please sign in to comment.