feat: display more information in mapping error message

The message will now display the source and the number of errors, and
even the original error message if only one error was encountered.
This commit is contained in:
Filippo Tessarotto 2022-07-26 22:55:32 +02:00 committed by GitHub
parent 2c1c7cf38a
commit 9c1e7c928b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 71 additions and 7 deletions

View File

@ -4,7 +4,9 @@ declare(strict_types=1);
namespace CuyZ\Valinor\Mapper;
use CuyZ\Valinor\Mapper\Tree\Message\MessagesFlattener;
use CuyZ\Valinor\Mapper\Tree\Node;
use CuyZ\Valinor\Utility\ValueDumper;
use RuntimeException;
/** @api */
@ -16,10 +18,21 @@ final class MappingError extends RuntimeException
{
$this->node = $node;
parent::__construct(
"Could not map type `{$node->type()}` with the given source.",
1617193185
);
$source = ValueDumper::dump($node->sourceValue());
$errors = (new MessagesFlattener($node))->errors();
$errorsCount = count($errors);
$body = "Could not map type `{$node->type()}` with value $source. A total of $errorsCount errors were encountered.";
if ($errorsCount === 1) {
$body = $errors->getIterator()->current()
->withParameter('root_type', $node->type())
->withBody("Could not map type `{root_type}`. An error occurred at path {node_path}: {original_message}")
->toString();
}
parent::__construct($body, 1617193185);
}
public function node(): Node

View File

@ -7,8 +7,8 @@ namespace CuyZ\Valinor\Mapper\Tree\Message;
use Countable;
use CuyZ\Valinor\Mapper\Tree\Node;
use CuyZ\Valinor\Mapper\Tree\NodeTraverser;
use Iterator;
use IteratorAggregate;
use Traversable;
use function array_filter;
use function count;
@ -66,9 +66,9 @@ final class MessagesFlattener implements IteratorAggregate, Countable
}
/**
* @return Traversable<NodeMessage>
* @return Iterator<NodeMessage>
*/
public function getIterator(): Traversable
public function getIterator(): Iterator
{
yield from $this->messages;
}

View File

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Tests\Integration\Mapping;
use CuyZ\Valinor\Mapper\MappingError;
use CuyZ\Valinor\MapperBuilder;
use CuyZ\Valinor\Tests\Integration\IntegrationTest;
final class MappingErrorTest extends IntegrationTest
{
public function test_mapping_error_code_is_correct(): void
{
try {
(new MapperBuilder())->mapper()->map('string', ['foo']);
self::fail();
} catch (MappingError $exception) {
self::assertSame(1617193185, $exception->getCode());
}
}
public function test_single_error_details_are_reported_in_exception_message(): void
{
try {
(new MapperBuilder())->mapper()->map('string', ['foo']);
self::fail();
} catch (MappingError $exception) {
self::assertSame("Could not map type `string`. An error occurred at path *root*: Value array{0: 'foo'} does not match type `string`.", $exception->getMessage());
}
}
public function test_several_errors_count_are_reported_in_exception_message(): void
{
try {
(new MapperBuilder())->mapper()->map(
'array{foo: string, bar: int}',
['foo' => 42, 'bar' => 'some string']
);
self::fail();
} catch (MappingError $exception) {
self::assertSame(
"Could not map type `array{foo: string, bar: int}` with value array{foo: 42, bar: 'some string'}. A total of 2 errors were encountered.",
$exception->getMessage()
);
}
}
}