Skip to content

Commit

Permalink
Added different message to distinguish between two cases:
Browse files Browse the repository at this point in the history
a) the value at the path is of the wrong type
b) the path does not exist

.
  • Loading branch information
Ivan Kurnosov committed Sep 25, 2024
1 parent 6683d60 commit 93af1e7
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 17 deletions.
32 changes: 26 additions & 6 deletions src/Psl/Type/Exception/AssertException.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,28 @@ final class AssertException extends Exception
/**
* @param list<string> $paths
*/
private function __construct(string $actual, string $expected, array $paths = [], ?Throwable $previous = null)
private function __construct(?string $actual, string $expected, array $paths = [], ?Throwable $previous = null)
{
$first = $previous instanceof Exception ? $previous->getFirstFailingActualType() : $actual;

parent::__construct(
Str\format(
if ($first !== null) {
$message = Str\format(
'Expected "%s", got "%s"%s.',
$expected,
$first,
$paths ? ' at path "' . Str\join($paths, '.') . '"' : ''
),
$actual,
$paths ? ' at path "' . Str\join($paths, '.') . '"' : '',
);
} else {
$message = Str\format(
'Expected "%s", received no value at path "%s".',
$expected,
Str\join($paths, '.'),
);
}

parent::__construct(
$message,
$actual ?? 'null',
$paths,
$previous
);
Expand All @@ -51,4 +61,14 @@ public static function withValue(

return new self(get_debug_type($value), $expected_type, Vec\filter_nulls($paths), $previous);
}

public static function withoutValue(
string $expected_type,
?string $path = null,
?Throwable $previous = null
): self {
$paths = $previous instanceof Exception ? [$path, ...$previous->getPaths()] : [$path];

return new self(null, $expected_type, Vec\filter_nulls($paths), $previous);
}
}
31 changes: 26 additions & 5 deletions src/Psl/Type/Exception/CoercionException.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,30 @@ final class CoercionException extends Exception
/**
* @param list<string> $paths
*/
private function __construct(string $actual, string $target, array $paths = [], ?Throwable $previous = null)
private function __construct(?string $actual, string $target, array $paths = [], ?Throwable $previous = null)
{
$first = $previous instanceof Exception ? $previous->getFirstFailingActualType() : $actual;

parent::__construct(
Str\format(
if ($first !== null) {
$message = Str\format(
'Could not coerce "%s" to type "%s"%s%s.',
$first,
$target,
$paths ? ' at path "' . Str\join($paths, '.') . '"' : '',
$previous && !$previous instanceof self ? ': ' . $previous->getMessage() : '',
),
$actual,
);
} else {
$message = Str\format(
'Could not coerce to type "%s" at path "%s" as the value was not passed%s.',
$target,
Str\join($paths, '.'),
$previous && !$previous instanceof self ? ': ' . $previous->getMessage() : '',
);
}

parent::__construct(
$message,
$actual ?? 'null',
$paths,
$previous
);
Expand All @@ -52,4 +63,14 @@ public static function withValue(

return new self(get_debug_type($value), $target, Vec\filter_nulls($paths), $previous);
}

public static function withoutValue(
string $target,
?string $path = null,
?Throwable $previous = null
): self {
$paths = $previous instanceof Exception ? [$path, ...$previous->getPaths()] : [$path];

return new self(null, $target, Vec\filter_nulls($paths), $previous);
}
}
4 changes: 2 additions & 2 deletions src/Psl/Type/Internal/ShapeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ private function coerceIterable(mixed $value): array
continue;
}

throw CoercionException::withValue(null, $this->toString(), PathExpression::path($element));
throw CoercionException::withoutValue($this->toString(), PathExpression::path($element));
}
} catch (CoercionException $e) {
throw match (true) {
Expand Down Expand Up @@ -196,7 +196,7 @@ public function assert(mixed $value): array
continue;
}

throw AssertException::withValue(null, $this->toString(), PathExpression::path($element));
throw AssertException::withoutValue($this->toString(), PathExpression::path($element));
}
} catch (AssertException $e) {
throw match (true) {
Expand Down
8 changes: 4 additions & 4 deletions tests/unit/Type/ShapeTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public static function provideAssertExceptionExpectations(): iterable
'name' => Type\string(),
]),
[],
'Expected "array{\'name\': string}", got "null" at path "name".'
'Expected "array{\'name\': string}", received no value at path "name".'
];
yield 'invalid key' => [
Type\shape([
Expand Down Expand Up @@ -247,7 +247,7 @@ public static function provideCoerceExceptionExpectations(): iterable
'name' => Type\string(),
]),
[],
'Could not coerce "null" to type "array{\'name\': string}" at path "name".'
'Could not coerce to type "array{\'name\': string}" at path "name" as the value was not passed.'
];
yield 'invalid key' => [
Type\shape([
Expand Down Expand Up @@ -295,7 +295,7 @@ public static function provideCoerceExceptionExpectations(): iterable
(static function () {
yield null => 'nope';
})(),
'Could not coerce "null" to type "array{\'id\': int}" at path "id".'
'Could not coerce to type "array{\'id\': int}" at path "id" as the value was not passed.'
];
yield 'iterator yielding object key' => [
Type\shape([
Expand All @@ -305,7 +305,7 @@ public static function provideCoerceExceptionExpectations(): iterable
yield (new class () {
}) => 'nope';
})(),
'Could not coerce "null" to type "array{\'id\': int}" at path "id".'
'Could not coerce to type "array{\'id\': int}" at path "id" as the value was not passed.'
];
}

Expand Down

0 comments on commit 93af1e7

Please sign in to comment.