Skip to content

Commit

Permalink
add path to coercion messages
Browse files Browse the repository at this point in the history
  • Loading branch information
vrielsa committed Oct 27, 2023
1 parent 0567d5d commit b1dd28c
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 12 deletions.
11 changes: 10 additions & 1 deletion src/Psl/Type/Exception/CoercionException.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Psl\Type\Exception;

use Psl\Iter;
use Psl\Str;
use Throwable;

Expand Down Expand Up @@ -40,7 +41,15 @@ public static function withValue(
string $target,
TypeTrace $typeTrace
): self {
return new self(get_debug_type($value), $target, $typeTrace);
return new self(
get_debug_type($value),
$target,
$typeTrace,
Iter\is_empty($typeTrace->getPath()) ? "" : Str\Format(
'at path %s',
Str\join($typeTrace->getPath(), '.')
)
);
}

public static function withConversionFailureOnValue(
Expand Down
24 changes: 24 additions & 0 deletions src/Psl/Type/Exception/TypeTrace.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ final class TypeTrace
*/
private array $frames = [];

/**
* @var list<string> $path
*/
private array $path = [];

public function withFrame(string $frame): self
{
$self = clone $this;
Expand All @@ -22,6 +27,15 @@ public function withFrame(string $frame): self
return $self;
}

public function withFrameAtPath(string $frame, string $path): self
{
$self = clone $this;
$self->frames[] = $frame;
$self->path[] = $path;

return $self;
}

/**
* @return list<string>
*
Expand All @@ -31,4 +45,14 @@ public function getFrames(): array
{
return $this->frames;
}

/**
* @return list<string>
*
* @psalm-mutation-free
*/
public function getPath(): array
{
return $this->path;
}
}
8 changes: 6 additions & 2 deletions src/Psl/Type/Internal/DictType.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,19 @@ public function coerce(mixed $value): array

$trace = $this->getTrace();
$key_type = $this->key_type->withTrace($trace->withFrame('dict<' . $this->key_type->toString() . ', _>'));
$value_type = $this->value_type->withTrace($trace->withFrame('dict<_, ' . $this->value_type->toString() . '>'));

$result = [];

/**
* @var Tk $k
* @var Tv $v
*/
foreach ($value as $k => $v) {
$value_type = $this->value_type->withTrace(
$trace->withFrameAtPath(
'dict<_, ' . $this->value_type->toString() . '>',
(string) $k
)
);
$result[$key_type->coerce($k)] = $value_type->coerce($v);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Psl/Type/Internal/ShapeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ private function getElementName(string|int $element): string
private function getTypeAndTraceForElement(string|int $element, Type\TypeInterface $type): array
{
$element_name = $this->getElementName($element);
$trace = $this->getTrace()->withFrame('array{' . $element_name . ': _}');
$trace = $this->getTrace()->withFrameAtPath('array{' . $element_name . ': _}', (string) $element);

return [
$trace,
Expand Down
18 changes: 10 additions & 8 deletions src/Psl/Type/Internal/VecType.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,21 @@ public function coerce(mixed $value): iterable
throw CoercionException::withValue($value, $this->toString(), $this->getTrace());
}

/** @var Type\Type<Tv> $value_type */
$value_type = $this->value_type->withTrace(
$this->getTrace()
->withFrame($this->toString())
);

/**
* @var list<Tv> $entries
*/
$result = [];

/** @var Tv $v */
foreach ($value as $v) {
/**
* @var array-key $i
* @var Tv $v
*/
foreach ($value as $i => $v) {
/** @var Type\Type<Tv> $value_type */
$value_type = $this->value_type->withTrace(
$this->getTrace()
->withFrameAtPath($this->toString(), (string) $i)
);
$result[] = $value_type->coerce($v);
}

Expand Down
25 changes: 25 additions & 0 deletions tests/unit/Type/Exception/TypeCoercionExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,29 @@ public function testConversionFailure(): void
static::assertCount(0, $frames);
}
}

public function testCoercionPath(): void
{
$type = Type\shape([
'articles' => Type\vec(Type\shape([
'name' => Type\string(),
]))
]);

try {
$type->coerce(['articles' => [['name' => null]]]);

static::fail(Str\format(
'Expected "%s" exception to be thrown.',
Type\Exception\CoercionException::class
));
} catch (Type\Exception\CoercionException $e) {
static::assertSame(
'Could not coerce "null" to type "string": at path articles.0.name',
$e->getMessage()
);

static::assertCount(3, $e->getTypeTrace()->getPath());
}
}
}

0 comments on commit b1dd28c

Please sign in to comment.