Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added different message to distinguish between two cases: #495

Open
wants to merge 3 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 30 additions & 12 deletions src/Psl/Type/Exception/AssertException.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,11 @@ 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, string $message, array $paths = [], ?Throwable $previous = null)
{
$first = $previous instanceof Exception ? $previous->getFirstFailingActualType() : $actual;

parent::__construct(
Str\format(
'Expected "%s", got "%s"%s.',
$expected,
$first,
$paths ? ' at path "' . Str\join($paths, '.') . '"' : ''
),
$actual,
$message,
$actual ?? 'null',
$paths,
$previous
);
Expand All @@ -47,8 +40,33 @@ public static function withValue(
?string $path = null,
?Throwable $previous = null
): self {
$paths = $previous instanceof Exception ? [$path, ...$previous->getPaths()] : [$path];
$paths = Vec\filter_nulls($previous instanceof Exception ? [$path, ...$previous->getPaths()] : [$path]);
$actual = get_debug_type($value);
$first = $previous instanceof Exception ? $previous->getFirstFailingActualType() : $actual;

$message = Str\format(
'Expected "%s", got "%s"%s.',
$expected_type,
$first,
$paths ? ' at path "' . Str\join($paths, '.') . '"' : '',
);

return new self($actual, $expected_type, $message, $paths, $previous);
}

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

$message = Str\format(
'Expected "%s", received no value at path "%s".',
$expected_type,
Str\join($paths, '.'),
);

return new self(get_debug_type($value), $expected_type, Vec\filter_nulls($paths), $previous);
return new self(null, $expected_type, $message, $paths, $previous);
}
}
60 changes: 47 additions & 13 deletions src/Psl/Type/Exception/CoercionException.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,22 @@ final class CoercionException extends Exception
{
private string $target;

private bool $withValue;

/**
* @param list<string> $paths
*/
private function __construct(string $actual, string $target, array $paths = [], ?Throwable $previous = null)
private function __construct(?string $actual, string $target, string $message, bool $withValue, array $paths = [], ?Throwable $previous = null)
{
$first = $previous instanceof Exception ? $previous->getFirstFailingActualType() : $actual;

parent::__construct(
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,
$message,
$actual ?? 'null',
$paths,
$previous
);

$this->target = $target;
$this->withValue = $withValue;
}

public function getTargetType(): string
Expand All @@ -48,8 +43,47 @@ public static function withValue(
?string $path = null,
?Throwable $previous = null
): self {
$paths = $previous instanceof Exception ? [$path, ...$previous->getPaths()] : [$path];
$paths = Vec\filter_nulls($previous instanceof Exception ? [$path, ...$previous->getPaths()] : [$path]);
$actual = get_debug_type($value);
$first = $previous instanceof Exception ? $previous->getFirstFailingActualType() : $actual;

$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() : '',
);

return new self($actual, $target, $message, true, $paths, $previous);
}

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

$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() : '',
);

return new self(null, $target, $message, false, $paths, $previous);
}

public function wrap(
mixed $value,
string $target,
?string $path = null
): self {
if ($this->withValue) {
return self::withValue($value, $target, $path, $this);
}

return new self(get_debug_type($value), $target, Vec\filter_nulls($paths), $previous);
return self::withoutValue($target, $path, $this);
}
}
6 changes: 3 additions & 3 deletions src/Psl/Type/Internal/ShapeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,11 @@ 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) {
$element_value_found => CoercionException::withValue($array[$element] ?? null, $this->toString(), PathExpression::path($element), $e),
$element_value_found => $e->wrap($array[$element] ?? null, $this->toString(), PathExpression::path($element)),
default => $e
};
}
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
19 changes: 15 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,18 @@ 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 'missing nested key' => [
Type\shape([
'a' => Type\shape([
'b' => Type\shape([
'c' => Type\mixed(),
]),
]),
]),
['a' => ['b' => []]],
'Could not coerce to type "array{\'a\': array{\'b\': array{\'c\': mixed}}}" at path "a.b.c" as the value was not passed.',
];
yield 'invalid key' => [
Type\shape([
Expand Down Expand Up @@ -295,7 +306,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 +316,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