mirror of
https://github.com/danog/Valinor.git
synced 2024-11-30 04:39:05 +01:00
feat!: refactor tree node API
The class `\CuyZ\Valinor\Mapper\Tree\Node` has been refactored to remove access to unwanted methods that were not supposed to be part of the public API. Below are a list of all changes: - New methods `$node->sourceFilled()` and `$node->sourceValue()` allow accessing the source value. - The method `$node->value()` has been renamed to `$node->mappedValue()` and will throw an exception if the node is not value. - The method `$node->type()` now returns a string. - The methods `$message->name()`, `$message->path()`, `$message->type()` and `$message->value()` have been deprecated in favor of the new method `$message->node()`. - The message parameter `{original_value}` has been deprecated in favor of `{source_value}`.
This commit is contained in:
parent
316d91910d
commit
d3b1dcb64e
@ -12,7 +12,7 @@ on the original message.
|
||||
| `{node_name}` | name of the node to which the message is bound |
|
||||
| `{node_path}` | path of the node to which the message is bound |
|
||||
| `{node_type}` | type of the node to which the message is bound |
|
||||
| `{original_value}` | the source value that was given to the node |
|
||||
| `{source_value}` | the source value that was given to the node |
|
||||
| `{original_message}` | the original message before being customized |
|
||||
|
||||
Usage:
|
||||
@ -46,9 +46,9 @@ try {
|
||||
} catch (\CuyZ\Valinor\Mapper\MappingError $error) {
|
||||
$message = $error->node()->messages()[0];
|
||||
|
||||
if (is_numeric($message->value())) {
|
||||
if (is_numeric($message->node()->mappedValue())) {
|
||||
$message = $message->withBody(
|
||||
'Invalid amount {original_value, number, currency}'
|
||||
'Invalid amount {source_value, number, currency}'
|
||||
);
|
||||
}
|
||||
|
||||
@ -133,7 +133,7 @@ In any case, the content can contain placeholders as described
|
||||
'Some message content' => 'New content / previous: {original_message}',
|
||||
|
||||
// Will match if the given message is an instance of `SomeError`
|
||||
SomeError::class => 'New content / value: {original_value}',
|
||||
SomeError::class => 'New content / value: {source_value}',
|
||||
|
||||
// A callback can be used to get access to the message instance
|
||||
OtherError::class => function (NodeMessage $message): string {
|
||||
|
@ -6,12 +6,9 @@ namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidTraversableKey;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\SourceMustBeIterable;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use CuyZ\Valinor\Type\CompositeTraversableType;
|
||||
|
||||
use CuyZ\Valinor\Type\Types\ArrayType;
|
||||
|
||||
use CuyZ\Valinor\Type\Types\IterableType;
|
||||
use CuyZ\Valinor\Type\Types\NonEmptyArrayType;
|
||||
|
||||
@ -28,7 +25,7 @@ final class ArrayNodeBuilder implements NodeBuilder
|
||||
$this->flexible = $flexible;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
$type = $shell->type();
|
||||
$value = $shell->hasValue() ? $shell->value() : null;
|
||||
@ -36,7 +33,7 @@ final class ArrayNodeBuilder implements NodeBuilder
|
||||
assert($type instanceof ArrayType || $type instanceof NonEmptyArrayType || $type instanceof IterableType);
|
||||
|
||||
if (null === $value && $this->flexible) {
|
||||
return Node::branch($shell, [], []);
|
||||
return TreeNode::branch($shell, [], []);
|
||||
}
|
||||
|
||||
if (! is_array($value)) {
|
||||
@ -46,11 +43,11 @@ final class ArrayNodeBuilder implements NodeBuilder
|
||||
$children = $this->children($type, $shell, $rootBuilder);
|
||||
$array = $this->buildArray($children);
|
||||
|
||||
return Node::branch($shell, $array, $children);
|
||||
return TreeNode::branch($shell, $array, $children);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Node>
|
||||
* @return array<TreeNode>
|
||||
*/
|
||||
private function children(CompositeTraversableType $type, Shell $shell, RootNodeBuilder $rootBuilder): array
|
||||
{
|
||||
@ -74,7 +71,7 @@ final class ArrayNodeBuilder implements NodeBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Node> $children
|
||||
* @param array<TreeNode> $children
|
||||
* @return mixed[]|null
|
||||
*/
|
||||
private function buildArray(array $children): ?array
|
||||
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\NoCasterForType;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
|
||||
/** @internal */
|
||||
@ -22,7 +21,7 @@ final class CasterNodeBuilder implements NodeBuilder
|
||||
$this->builders = $builders;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
$type = $shell->type();
|
||||
|
||||
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
|
||||
/** @internal */
|
||||
@ -17,13 +16,13 @@ final class CasterProxyNodeBuilder implements NodeBuilder
|
||||
$this->delegate = $delegate;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
if ($shell->hasValue()) {
|
||||
$value = $shell->value();
|
||||
|
||||
if ($shell->type()->accepts($value)) {
|
||||
return Node::leaf($shell, $value);
|
||||
return TreeNode::leaf($shell, $value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,6 @@ use CuyZ\Valinor\Mapper\Object\FilledArguments;
|
||||
use CuyZ\Valinor\Mapper\Object\FilteredObjectBuilder;
|
||||
use CuyZ\Valinor\Mapper\Object\ObjectBuilder;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\UnexpectedArrayKeysForClass;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Type\Types\ClassType;
|
||||
@ -41,7 +40,7 @@ final class ClassNodeBuilder implements NodeBuilder
|
||||
$this->flexible = $flexible;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
$classTypes = $this->classTypes($shell->type());
|
||||
|
||||
@ -70,7 +69,7 @@ final class ClassNodeBuilder implements NodeBuilder
|
||||
|
||||
$object = $this->buildObject($builder, $children);
|
||||
|
||||
$node = Node::branch($shell, $object, $children);
|
||||
$node = TreeNode::branch($shell, $object, $children);
|
||||
|
||||
if (! $this->flexible) {
|
||||
$node = $this->checkForUnexpectedKeys($arguments, $node);
|
||||
@ -109,7 +108,7 @@ final class ClassNodeBuilder implements NodeBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node[] $children
|
||||
* @param TreeNode[] $children
|
||||
*/
|
||||
private function buildObject(ObjectBuilder $builder, array $children): ?object
|
||||
{
|
||||
@ -126,7 +125,7 @@ final class ClassNodeBuilder implements NodeBuilder
|
||||
return $builder->build($arguments);
|
||||
}
|
||||
|
||||
private function checkForUnexpectedKeys(FilledArguments $arguments, Node $node): Node
|
||||
private function checkForUnexpectedKeys(FilledArguments $arguments, TreeNode $node): TreeNode
|
||||
{
|
||||
$superfluousKeys = $arguments->superfluousKeys();
|
||||
|
||||
|
@ -6,7 +6,6 @@ namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use BackedEnum;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidEnumValue;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use CuyZ\Valinor\Type\Types\EnumType;
|
||||
use Stringable;
|
||||
@ -28,7 +27,7 @@ final class EnumNodeBuilder implements NodeBuilder
|
||||
$this->flexible = $flexible;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
$type = $shell->type();
|
||||
$value = $shell->value();
|
||||
@ -37,7 +36,7 @@ final class EnumNodeBuilder implements NodeBuilder
|
||||
|
||||
foreach ($type->className()::cases() as $case) {
|
||||
if ($this->valueMatchesEnumCase($value, $case)) {
|
||||
return Node::leaf($shell, $case);
|
||||
return TreeNode::leaf($shell, $case);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\ErrorMessage;
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\Message;
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\UserlandError;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use Throwable;
|
||||
|
||||
@ -28,7 +27,7 @@ final class ErrorCatcherNodeBuilder implements NodeBuilder
|
||||
$this->exceptionFilter = $exceptionFilter;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
try {
|
||||
return $this->delegate->build($shell, $rootBuilder);
|
||||
@ -37,7 +36,7 @@ final class ErrorCatcherNodeBuilder implements NodeBuilder
|
||||
$exception = ($this->exceptionFilter)($exception->previous());
|
||||
}
|
||||
|
||||
return Node::error($shell, $exception);
|
||||
return TreeNode::error($shell, $exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ use CuyZ\Valinor\Mapper\Object\Factory\ObjectBuilderFactory;
|
||||
use CuyZ\Valinor\Mapper\Object\FilledArguments;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\ObjectImplementationCallbackError;
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\UserlandError;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use CuyZ\Valinor\Type\Types\ClassType;
|
||||
use CuyZ\Valinor\Type\Types\InterfaceType;
|
||||
@ -44,7 +43,7 @@ final class InterfaceNodeBuilder implements NodeBuilder
|
||||
$this->flexible = $flexible;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
$type = $shell->type();
|
||||
|
||||
@ -63,7 +62,7 @@ final class InterfaceNodeBuilder implements NodeBuilder
|
||||
|
||||
foreach ($children as $child) {
|
||||
if (! $child->isValid()) {
|
||||
return Node::branch($shell, null, $children);
|
||||
return TreeNode::branch($shell, null, $children);
|
||||
}
|
||||
|
||||
$values[] = $child->value();
|
||||
@ -82,7 +81,7 @@ final class InterfaceNodeBuilder implements NodeBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Node[]
|
||||
* @return TreeNode[]
|
||||
*/
|
||||
private function children(Shell $shell, FilledArguments $arguments, RootNodeBuilder $rootBuilder): array
|
||||
{
|
||||
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
|
||||
use function is_array;
|
||||
@ -21,7 +20,7 @@ final class IterableNodeBuilder implements NodeBuilder
|
||||
$this->delegate = $delegate;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
if ($shell->hasValue()) {
|
||||
$value = $shell->value();
|
||||
|
@ -6,7 +6,6 @@ namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidListKey;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\SourceMustBeIterable;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use CuyZ\Valinor\Type\CompositeTraversableType;
|
||||
use CuyZ\Valinor\Type\Types\ListType;
|
||||
@ -25,7 +24,7 @@ final class ListNodeBuilder implements NodeBuilder
|
||||
$this->flexible = $flexible;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
$type = $shell->type();
|
||||
$value = $shell->hasValue() ? $shell->value() : null;
|
||||
@ -33,7 +32,7 @@ final class ListNodeBuilder implements NodeBuilder
|
||||
assert($type instanceof ListType || $type instanceof NonEmptyListType);
|
||||
|
||||
if (null === $value && $this->flexible) {
|
||||
return Node::branch($shell, [], []);
|
||||
return TreeNode::branch($shell, [], []);
|
||||
}
|
||||
|
||||
if (! is_array($value)) {
|
||||
@ -43,11 +42,11 @@ final class ListNodeBuilder implements NodeBuilder
|
||||
$children = $this->children($type, $shell, $rootBuilder);
|
||||
$array = $this->buildArray($children);
|
||||
|
||||
return Node::branch($shell, $array, $children);
|
||||
return TreeNode::branch($shell, $array, $children);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Node>
|
||||
* @return array<TreeNode>
|
||||
*/
|
||||
private function children(CompositeTraversableType $type, Shell $shell, RootNodeBuilder $rootBuilder): array
|
||||
{
|
||||
@ -64,7 +63,7 @@ final class ListNodeBuilder implements NodeBuilder
|
||||
$children[$expected] = $rootBuilder->build($child->withValue($value));
|
||||
} else {
|
||||
$child = $shell->child((string)$key, $subType);
|
||||
$children[$key] = Node::error($child, new InvalidListKey($key, $expected));
|
||||
$children[$key] = TreeNode::error($child, new InvalidListKey($key, $expected));
|
||||
}
|
||||
|
||||
$expected++;
|
||||
@ -74,7 +73,7 @@ final class ListNodeBuilder implements NodeBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Node> $children
|
||||
* @param array<TreeNode> $children
|
||||
* @return mixed[]|null
|
||||
*/
|
||||
private function buildArray(array $children): ?array
|
||||
|
@ -2,11 +2,10 @@
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
|
||||
/** @internal */
|
||||
interface NodeBuilder
|
||||
{
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node;
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode;
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
|
||||
/** @internal */
|
||||
@ -17,7 +16,7 @@ final class RootNodeBuilder
|
||||
$this->root = $root;
|
||||
}
|
||||
|
||||
public function build(Shell $shell): Node
|
||||
public function build(Shell $shell): TreeNode
|
||||
{
|
||||
return $this->root->build($shell, $this);
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\CannotCastToScalarValue;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\ValueNotAcceptedByScalarType;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use CuyZ\Valinor\Type\ScalarType;
|
||||
|
||||
@ -22,7 +21,7 @@ final class ScalarNodeBuilder implements NodeBuilder
|
||||
$this->flexible = $flexible;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
$type = $shell->type();
|
||||
$value = $shell->hasValue() ? $shell->value() : null;
|
||||
@ -37,6 +36,6 @@ final class ScalarNodeBuilder implements NodeBuilder
|
||||
throw new CannotCastToScalarValue($value, $type);
|
||||
}
|
||||
|
||||
return Node::leaf($shell, $type->cast($value));
|
||||
return TreeNode::leaf($shell, $type->cast($value));
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\ShapedArrayElementMissing;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\SourceMustBeIterable;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\UnexpectedShapedArrayKeys;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use CuyZ\Valinor\Type\Types\ShapedArrayType;
|
||||
|
||||
@ -27,7 +26,7 @@ final class ShapedArrayNodeBuilder implements NodeBuilder
|
||||
$this->flexible = $flexible;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
$type = $shell->type();
|
||||
$value = $shell->hasValue() ? $shell->value() : null;
|
||||
@ -41,11 +40,11 @@ final class ShapedArrayNodeBuilder implements NodeBuilder
|
||||
$children = $this->children($type, $shell, $rootBuilder);
|
||||
$array = $this->buildArray($children);
|
||||
|
||||
return Node::branch($shell, $array, $children);
|
||||
return TreeNode::branch($shell, $array, $children);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Node>
|
||||
* @return array<TreeNode>
|
||||
*/
|
||||
private function children(ShapedArrayType $type, Shell $shell, RootNodeBuilder $rootBuilder): array
|
||||
{
|
||||
@ -62,7 +61,7 @@ final class ShapedArrayNodeBuilder implements NodeBuilder
|
||||
|
||||
if (! array_key_exists($key, $value)) {
|
||||
if (! $element->isOptional()) {
|
||||
$children[$key] = Node::error($child, new ShapedArrayElementMissing($element));
|
||||
$children[$key] = TreeNode::error($child, new ShapedArrayElementMissing($element));
|
||||
}
|
||||
|
||||
continue;
|
||||
@ -82,7 +81,7 @@ final class ShapedArrayNodeBuilder implements NodeBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Node> $children
|
||||
* @param array<TreeNode> $children
|
||||
* @return mixed[]|null
|
||||
*/
|
||||
private function buildArray(array $children): ?array
|
||||
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use CuyZ\Valinor\Mapper\Tree\Visitor\ShellVisitor;
|
||||
|
||||
@ -22,7 +21,7 @@ final class ShellVisitorNodeBuilder implements NodeBuilder
|
||||
$this->visitors = $visitors;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
foreach ($this->visitors as $visitor) {
|
||||
$shell = $visitor->visit($shell);
|
||||
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\MissingNodeValue;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use CuyZ\Valinor\Utility\TypeHelper;
|
||||
|
||||
@ -22,7 +21,7 @@ final class StrictNodeBuilder implements NodeBuilder
|
||||
$this->flexible = $flexible;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
if (! $this->flexible) {
|
||||
TypeHelper::checkPermissiveType($shell->type());
|
||||
@ -30,7 +29,7 @@ final class StrictNodeBuilder implements NodeBuilder
|
||||
|
||||
if (! $shell->hasValue()) {
|
||||
if ($this->flexible && $shell->type()->accepts(null)) {
|
||||
return Node::leaf($shell, null);
|
||||
return TreeNode::leaf($shell, null);
|
||||
}
|
||||
|
||||
throw new MissingNodeValue($shell->type());
|
||||
|
169
src/Mapper/Tree/Builder/TreeNode.php
Normal file
169
src/Mapper/Tree/Builder/TreeNode.php
Normal file
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\DuplicatedNodeChild;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidNodeValue;
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\Message;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use Throwable;
|
||||
|
||||
use function assert;
|
||||
|
||||
/** @internal */
|
||||
final class TreeNode
|
||||
{
|
||||
private Shell $shell;
|
||||
|
||||
/** @var mixed */
|
||||
private $value;
|
||||
|
||||
/** @var array<self> */
|
||||
private array $children = [];
|
||||
|
||||
/** @var array<Message> */
|
||||
private array $messages = [];
|
||||
|
||||
private bool $valid = true;
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
private function __construct(Shell $shell, $value)
|
||||
{
|
||||
$this->shell = $shell;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function leaf(Shell $shell, $value): self
|
||||
{
|
||||
$instance = new self($shell, $value);
|
||||
$instance->check();
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array<self> $children
|
||||
*/
|
||||
public static function branch(Shell $shell, $value, array $children): self
|
||||
{
|
||||
$instance = new self($shell, $value);
|
||||
|
||||
foreach ($children as $child) {
|
||||
$name = $child->name();
|
||||
|
||||
if (isset($instance->children[$name])) {
|
||||
throw new DuplicatedNodeChild($name);
|
||||
}
|
||||
|
||||
$instance->children[$name] = $child;
|
||||
}
|
||||
|
||||
$instance->check();
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Throwable&Message $message
|
||||
*/
|
||||
public static function error(Shell $shell, Throwable $message): self
|
||||
{
|
||||
return (new self($shell, null))->withMessage($message);
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return $this->shell->name();
|
||||
}
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
return $this->valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function withValue($value): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->value = $value;
|
||||
$clone->check();
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function value()
|
||||
{
|
||||
assert($this->valid, "Trying to get value of an invalid node at path `{$this->shell->path()}`.");
|
||||
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function withMessage(Message $message): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->messages[] = $message;
|
||||
$clone->valid = $clone->valid && ! $message instanceof Throwable;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<self>
|
||||
*/
|
||||
public function children(): array
|
||||
{
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
public function node(): Node
|
||||
{
|
||||
return $this->buildNode($this);
|
||||
}
|
||||
|
||||
private function check(): void
|
||||
{
|
||||
foreach ($this->children as $child) {
|
||||
if (! $child->valid) {
|
||||
$this->valid = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->valid && ! $this->shell->type()->accepts($this->value)) {
|
||||
throw new InvalidNodeValue($this->value, $this->shell->type());
|
||||
}
|
||||
}
|
||||
|
||||
private function buildNode(self $self): Node
|
||||
{
|
||||
return new Node(
|
||||
$self->shell->isRoot(),
|
||||
$self->shell->name(),
|
||||
$self->shell->path(),
|
||||
(string)$self->shell->type(),
|
||||
$this->shell->hasValue(),
|
||||
$this->shell->hasValue() ? $this->shell->value() : null,
|
||||
$self->valid ? $self->value : null,
|
||||
$self->messages,
|
||||
array_map(
|
||||
fn (self $child) => $self->buildNode($child),
|
||||
$self->children
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use CuyZ\Valinor\Type\Resolver\Union\UnionNarrower;
|
||||
use CuyZ\Valinor\Type\Types\UnionType;
|
||||
@ -22,7 +21,7 @@ final class UnionNodeBuilder implements NodeBuilder
|
||||
$this->unionNarrower = $unionNarrower;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
$type = $shell->type();
|
||||
|
||||
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Definition\FunctionsContainer;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
|
||||
/** @internal */
|
||||
@ -21,7 +20,7 @@ final class ValueAlteringNodeBuilder implements NodeBuilder
|
||||
$this->functions = $functions;
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
$node = $this->delegate->build($shell, $rootBuilder);
|
||||
|
||||
|
@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Exception;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use LogicException;
|
||||
|
||||
/** @internal */
|
||||
final class CannotGetInvalidNodeValue extends LogicException
|
||||
{
|
||||
public function __construct(Node $node)
|
||||
{
|
||||
parent::__construct(
|
||||
"Trying to get value of an invalid node at path `{$node->path()}`.",
|
||||
1630680246
|
||||
);
|
||||
}
|
||||
}
|
19
src/Mapper/Tree/Exception/InvalidNodeHasNoMappedValue.php
Normal file
19
src/Mapper/Tree/Exception/InvalidNodeHasNoMappedValue.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/** @internal */
|
||||
final class InvalidNodeHasNoMappedValue extends RuntimeException
|
||||
{
|
||||
public function __construct(string $path)
|
||||
{
|
||||
parent::__construct(
|
||||
"Cannot get mapped value for invalid node at path `$path`; use method `\$node->isValid()`.",
|
||||
1657466305
|
||||
);
|
||||
}
|
||||
}
|
19
src/Mapper/Tree/Exception/SourceValueWasNotFilled.php
Normal file
19
src/Mapper/Tree/Exception/SourceValueWasNotFilled.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/** @internal */
|
||||
final class SourceValueWasNotFilled extends RuntimeException
|
||||
{
|
||||
public function __construct(string $path)
|
||||
{
|
||||
parent::__construct(
|
||||
"Source was not filled at path `$path`; use method `\$node->sourceFilled()`.",
|
||||
1657466107
|
||||
);
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ use function is_callable;
|
||||
* 'Some message content' => 'New content / previous: {original_message}',
|
||||
*
|
||||
* // Will match if the given message is an instance of `SomeError`
|
||||
* SomeError::class => 'New content / value: {original_value}',
|
||||
* SomeError::class => 'New content / value: {source_value}',
|
||||
*
|
||||
* // A callback can be used to get access to the message instance
|
||||
* OtherError::class => function (NodeMessage $message): string {
|
||||
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Message\Formatter;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\NodeMessage;
|
||||
use CuyZ\Valinor\Utility\TypeHelper;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
@ -44,9 +43,9 @@ final class PlaceHolderMessageFormatter implements MessageFormatter
|
||||
$body = sprintf($message->body(), ...$this->values ?: [
|
||||
$message->code(),
|
||||
$originalMessage instanceof Throwable ? $originalMessage->getMessage() : $originalMessage->__toString(),
|
||||
TypeHelper::dump($message->type()),
|
||||
$message->name(),
|
||||
$message->path(),
|
||||
"`{$message->node()->type()}`",
|
||||
$message->node()->name(),
|
||||
$message->node()->path(),
|
||||
]);
|
||||
|
||||
return $message->withBody($body);
|
||||
|
@ -4,18 +4,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree\Message;
|
||||
|
||||
use CuyZ\Valinor\Definition\Attributes;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Utility\String\StringFormatter;
|
||||
use CuyZ\Valinor\Utility\TypeHelper;
|
||||
use CuyZ\Valinor\Utility\ValueDumper;
|
||||
use Throwable;
|
||||
|
||||
/** @api */
|
||||
final class NodeMessage implements Message, HasCode
|
||||
{
|
||||
private Shell $shell;
|
||||
private Node $node;
|
||||
|
||||
private Message $message;
|
||||
|
||||
@ -23,9 +20,9 @@ final class NodeMessage implements Message, HasCode
|
||||
|
||||
private string $locale = StringFormatter::DEFAULT_LOCALE;
|
||||
|
||||
public function __construct(Shell $shell, Message $message)
|
||||
public function __construct(Node $node, Message $message)
|
||||
{
|
||||
$this->shell = $shell;
|
||||
$this->node = $node;
|
||||
$this->message = $message;
|
||||
|
||||
if ($this->message instanceof TranslatableMessage) {
|
||||
@ -37,6 +34,11 @@ final class NodeMessage implements Message, HasCode
|
||||
}
|
||||
}
|
||||
|
||||
public function node(): Node
|
||||
{
|
||||
return $this->node;
|
||||
}
|
||||
|
||||
public function withLocale(string $locale): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
@ -63,32 +65,38 @@ final class NodeMessage implements Message, HasCode
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `$message->node()->name()` instead
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->shell->name();
|
||||
}
|
||||
|
||||
public function path(): string
|
||||
{
|
||||
return $this->shell->path();
|
||||
}
|
||||
|
||||
public function type(): Type
|
||||
{
|
||||
return $this->shell->type();
|
||||
}
|
||||
|
||||
public function attributes(): Attributes
|
||||
{
|
||||
return $this->shell->attributes();
|
||||
return $this->node->name();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `$message->node()->path()` instead
|
||||
*/
|
||||
public function path(): string
|
||||
{
|
||||
return $this->node->path();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `$message->node()->type()` instead
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return $this->node->type();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `$message->node()->mappedValue()` instead
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function value()
|
||||
{
|
||||
return $this->shell->value();
|
||||
return $this->node->mappedValue();
|
||||
}
|
||||
|
||||
public function originalMessage(): Message
|
||||
@ -126,10 +134,11 @@ final class NodeMessage implements Message, HasCode
|
||||
{
|
||||
$parameters = [
|
||||
'message_code' => $this->code(),
|
||||
'node_name' => $this->shell->name(),
|
||||
'node_path' => $this->shell->path(),
|
||||
'node_type' => TypeHelper::dump($this->shell->type()),
|
||||
'original_value' => ValueDumper::dump($this->shell->hasValue() ? $this->shell->value() : '*missing*'),
|
||||
'node_name' => $this->node->name(),
|
||||
'node_path' => $this->node->path(),
|
||||
'node_type' => "`{$this->node->type()}`",
|
||||
'source_value' => $sourceValue = $this->node->sourceFilled() ? ValueDumper::dump($this->node->sourceValue()) : '*missing*',
|
||||
'original_value' => $sourceValue, // @deprecated
|
||||
'original_message' => $this->message instanceof Throwable ? $this->message->getMessage() : $this->message->__toString(),
|
||||
];
|
||||
|
||||
|
@ -4,148 +4,134 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Mapper\Tree;
|
||||
|
||||
use CuyZ\Valinor\Definition\Attributes;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\CannotGetInvalidNodeValue;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\DuplicatedNodeChild;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidNodeValue;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidNodeHasNoMappedValue;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\SourceValueWasNotFilled;
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\Message;
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\NodeMessage;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use Throwable;
|
||||
|
||||
/** @api */
|
||||
final class Node
|
||||
{
|
||||
private Shell $shell;
|
||||
private bool $isRoot;
|
||||
|
||||
private string $name;
|
||||
|
||||
private string $path;
|
||||
|
||||
private string $type;
|
||||
|
||||
private bool $sourceFilled;
|
||||
|
||||
/** @var mixed */
|
||||
private $value;
|
||||
private $sourceValue;
|
||||
|
||||
/** @var array<Node> */
|
||||
private array $children = [];
|
||||
private bool $isValid = true;
|
||||
|
||||
/** @var mixed */
|
||||
private $mappedValue;
|
||||
|
||||
/** @var array<NodeMessage> */
|
||||
private array $messages = [];
|
||||
|
||||
private bool $valid = true;
|
||||
/** @var array<self> */
|
||||
private array $children;
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $sourceValue
|
||||
* @param mixed $mappedValue
|
||||
* @param array<Message> $messages
|
||||
* @param array<self> $children
|
||||
*/
|
||||
private function __construct(Shell $shell, $value)
|
||||
{
|
||||
$this->shell = $shell;
|
||||
$this->value = $value;
|
||||
}
|
||||
public function __construct(
|
||||
bool $isRoot,
|
||||
string $name,
|
||||
string $path,
|
||||
string $type,
|
||||
bool $sourceFilled,
|
||||
$sourceValue,
|
||||
$mappedValue,
|
||||
array $messages,
|
||||
array $children
|
||||
) {
|
||||
$this->isRoot = $isRoot;
|
||||
$this->name = $name;
|
||||
$this->path = $path;
|
||||
$this->type = $type;
|
||||
$this->sourceFilled = $sourceFilled;
|
||||
$this->sourceValue = $sourceValue;
|
||||
$this->mappedValue = $mappedValue;
|
||||
$this->children = $children;
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function leaf(Shell $shell, $value): self
|
||||
{
|
||||
$instance = new self($shell, $value);
|
||||
$instance->check();
|
||||
foreach ($messages as $message) {
|
||||
$message = new NodeMessage($this, $message);
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param array<Node> $children
|
||||
*/
|
||||
public static function branch(Shell $shell, $value, array $children): self
|
||||
{
|
||||
$instance = new self($shell, $value);
|
||||
|
||||
foreach ($children as $child) {
|
||||
$name = $child->name();
|
||||
|
||||
if (isset($instance->children[$name])) {
|
||||
throw new DuplicatedNodeChild($name);
|
||||
}
|
||||
|
||||
$instance->children[$name] = $child;
|
||||
$this->messages[] = $message;
|
||||
$this->isValid = $this->isValid && ! $message->isError();
|
||||
}
|
||||
|
||||
$instance->check();
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Throwable&Message $message
|
||||
*/
|
||||
public static function error(Shell $shell, Throwable $message): self
|
||||
{
|
||||
return (new self($shell, null))->withMessage($message);
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return $this->shell->name();
|
||||
}
|
||||
|
||||
public function isRoot(): bool
|
||||
{
|
||||
return $this->shell->isRoot();
|
||||
return $this->isRoot;
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function path(): string
|
||||
{
|
||||
return $this->shell->path();
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function type(): Type
|
||||
public function type(): string
|
||||
{
|
||||
return $this->shell->type();
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function attributes(): Attributes
|
||||
public function sourceFilled(): bool
|
||||
{
|
||||
return $this->shell->attributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function withValue($value): self
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->value = $value;
|
||||
$clone->check();
|
||||
|
||||
return $clone;
|
||||
return $this->sourceFilled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function value()
|
||||
public function sourceValue()
|
||||
{
|
||||
if (! $this->valid) {
|
||||
throw new CannotGetInvalidNodeValue($this);
|
||||
if (! $this->sourceFilled) {
|
||||
throw new SourceValueWasNotFilled($this->path);
|
||||
}
|
||||
|
||||
return $this->value;
|
||||
return $this->sourceValue;
|
||||
}
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
return $this->isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Node>
|
||||
* @return mixed
|
||||
*/
|
||||
public function children(): array
|
||||
public function mappedValue()
|
||||
{
|
||||
return $this->children;
|
||||
if (! $this->isValid) {
|
||||
throw new InvalidNodeHasNoMappedValue($this->path);
|
||||
}
|
||||
|
||||
return $this->mappedValue;
|
||||
}
|
||||
|
||||
public function withMessage(Message $message): self
|
||||
/**
|
||||
* @deprecated use `$node->mappedValue()` instead
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function value()
|
||||
{
|
||||
$message = new NodeMessage($this->shell, $message);
|
||||
|
||||
$clone = clone $this;
|
||||
$clone->messages[] = $message;
|
||||
$clone->valid = $clone->valid && ! $message->isError();
|
||||
|
||||
return $clone;
|
||||
return $this->mappedValue();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,23 +142,11 @@ final class Node
|
||||
return $this->messages;
|
||||
}
|
||||
|
||||
public function isValid(): bool
|
||||
/**
|
||||
* @return array<self>
|
||||
*/
|
||||
public function children(): array
|
||||
{
|
||||
return $this->valid;
|
||||
}
|
||||
|
||||
private function check(): void
|
||||
{
|
||||
foreach ($this->children as $child) {
|
||||
if (! $child->valid) {
|
||||
$this->valid = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->valid && ! $this->shell->type()->accepts($this->value)) {
|
||||
throw new InvalidNodeValue($this->value, $this->shell->type());
|
||||
}
|
||||
return $this->children;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ namespace CuyZ\Valinor\Mapper;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Exception\InvalidMappingTypeSignature;
|
||||
use CuyZ\Valinor\Mapper\Tree\Builder\RootNodeBuilder;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Builder\TreeNode;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
|
||||
use CuyZ\Valinor\Type\Parser\TypeParser;
|
||||
@ -29,7 +29,7 @@ final class TreeMapperContainer implements TreeMapper
|
||||
$node = $this->node($signature, $source);
|
||||
|
||||
if (! $node->isValid()) {
|
||||
throw new MappingError($node);
|
||||
throw new MappingError($node->node());
|
||||
}
|
||||
|
||||
return $node->value();
|
||||
@ -38,7 +38,7 @@ final class TreeMapperContainer implements TreeMapper
|
||||
/**
|
||||
* @param mixed $source
|
||||
*/
|
||||
private function node(string $signature, $source): Node
|
||||
private function node(string $signature, $source): TreeNode
|
||||
{
|
||||
try {
|
||||
$type = $this->typeParser->parse($signature);
|
||||
|
@ -6,16 +6,16 @@ namespace CuyZ\Valinor\Tests\Fake\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Builder\NodeBuilder;
|
||||
use CuyZ\Valinor\Mapper\Tree\Builder\RootNodeBuilder;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\Builder\TreeNode;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
|
||||
final class FakeNodeBuilder implements NodeBuilder
|
||||
{
|
||||
/** @var null|callable(Shell): Node */
|
||||
/** @var null|callable(Shell): TreeNode */
|
||||
private $callback;
|
||||
|
||||
/**
|
||||
* @param null|callable(Shell): Node $callback
|
||||
* @param null|callable(Shell): TreeNode $callback
|
||||
*/
|
||||
public function __construct(callable $callback = null)
|
||||
{
|
||||
@ -24,12 +24,12 @@ final class FakeNodeBuilder implements NodeBuilder
|
||||
}
|
||||
}
|
||||
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): Node
|
||||
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
|
||||
{
|
||||
if (isset($this->callback)) {
|
||||
return ($this->callback)($shell);
|
||||
}
|
||||
|
||||
return Node::leaf($shell, $shell->value());
|
||||
return TreeNode::leaf($shell, $shell->value());
|
||||
}
|
||||
}
|
||||
|
@ -2,20 +2,21 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Fake\Mapper;
|
||||
namespace CuyZ\Valinor\Tests\Fake\Mapper\Tree\Builder;
|
||||
|
||||
use CuyZ\Valinor\Definition\Attributes;
|
||||
use CuyZ\Valinor\Mapper\Tree\Builder\TreeNode;
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\Message;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Tests\Fake\Definition\FakeAttributes;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\FakeShell;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeErrorMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use CuyZ\Valinor\Type\Type;
|
||||
use Throwable;
|
||||
|
||||
final class FakeNode
|
||||
final class FakeTreeNode
|
||||
{
|
||||
public static function any(): Node
|
||||
public static function any(): TreeNode
|
||||
{
|
||||
return self::leaf(FakeType::permissive(), []);
|
||||
}
|
||||
@ -23,18 +24,18 @@ final class FakeNode
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function leaf(Type $type, $value): Node
|
||||
public static function leaf(Type $type, $value): TreeNode
|
||||
{
|
||||
$shell = FakeShell::new($type, $value);
|
||||
|
||||
return Node::leaf($shell, $value);
|
||||
return TreeNode::leaf($shell, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array{name?: string, type?: Type, value?: mixed, attributes?: Attributes, message?: Message}> $children
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function branch(array $children, Type $type = null, $value = null): Node
|
||||
public static function branch(array $children, Type $type = null, $value = null): TreeNode
|
||||
{
|
||||
$shell = FakeShell::new($type ?? FakeType::permissive(), $value);
|
||||
$nodes = [];
|
||||
@ -47,7 +48,7 @@ final class FakeNode
|
||||
$child['attributes'] ?? new FakeAttributes(),
|
||||
)->withValue($childValue);
|
||||
|
||||
$node = Node::leaf($childShell, $childValue);
|
||||
$node = TreeNode::leaf($childShell, $childValue);
|
||||
|
||||
if (isset($child['message'])) {
|
||||
$node = $node->withMessage($child['message']);
|
||||
@ -56,16 +57,16 @@ final class FakeNode
|
||||
$nodes[] = $node;
|
||||
}
|
||||
|
||||
return Node::branch($shell, $value, $nodes);
|
||||
return TreeNode::branch($shell, $value, $nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Throwable&Message $error
|
||||
*/
|
||||
public static function error(Throwable $error = null): Node
|
||||
public static function error(Throwable $error = null): TreeNode
|
||||
{
|
||||
$shell = FakeShell::new(FakeType::permissive(), []);
|
||||
|
||||
return Node::error($shell, $error ?? new FakeErrorMessage());
|
||||
return TreeNode::error($shell, $error ?? new FakeErrorMessage());
|
||||
}
|
||||
}
|
48
tests/Fake/Mapper/Tree/FakeNode.php
Normal file
48
tests/Fake/Mapper/Tree/FakeNode.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Fake\Mapper\Tree;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\Message;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
|
||||
final class FakeNode
|
||||
{
|
||||
public static function any(): Node
|
||||
{
|
||||
return self::build([], []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Node> $children
|
||||
*/
|
||||
public static function branch(array $children): Node
|
||||
{
|
||||
return self::build([], $children);
|
||||
}
|
||||
|
||||
public static function withMessage(Message $message): Node
|
||||
{
|
||||
return self::build([$message], []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Message> $messages
|
||||
* @param array<Node> $children
|
||||
*/
|
||||
private static function build(array $messages, array $children): Node
|
||||
{
|
||||
return new Node(
|
||||
true,
|
||||
'nodeName',
|
||||
'some.node.path',
|
||||
'string',
|
||||
true,
|
||||
'some source value',
|
||||
'some value',
|
||||
$messages,
|
||||
$children,
|
||||
);
|
||||
}
|
||||
}
|
@ -6,17 +6,25 @@ namespace CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\Message;
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\NodeMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\FakeShell;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\FakeNode;
|
||||
|
||||
final class FakeNodeMessage
|
||||
{
|
||||
public static function any(): NodeMessage
|
||||
{
|
||||
return self::with(new FakeMessage());
|
||||
return self::build(new FakeMessage());
|
||||
}
|
||||
|
||||
public static function with(Message $message): NodeMessage
|
||||
public static function withMessage(Message $message): NodeMessage
|
||||
{
|
||||
return new NodeMessage(FakeShell::any(), $message);
|
||||
return self::build($message);
|
||||
}
|
||||
|
||||
private static function build(Message $message): NodeMessage
|
||||
{
|
||||
return new NodeMessage(
|
||||
FakeNode::any(),
|
||||
$message
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Tests\Unit\Mapper;
|
||||
|
||||
use CuyZ\Valinor\Mapper\MappingError;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\FakeNode;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\FakeNode;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class MappingErrorTest extends TestCase
|
||||
|
182
tests/Unit/Mapper/Tree/Builder/TreeNodeTest.php
Normal file
182
tests/Unit/Mapper/Tree/Builder/TreeNodeTest.php
Normal file
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Unit\Mapper\Tree\Builder;
|
||||
|
||||
use AssertionError;
|
||||
use CuyZ\Valinor\Mapper\Tree\Builder\TreeNode;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\DuplicatedNodeChild;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidNodeValue;
|
||||
use CuyZ\Valinor\Mapper\Tree\Shell;
|
||||
use CuyZ\Valinor\Tests\Fake\Definition\FakeAttributes;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Builder\FakeTreeNode;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeErrorMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeObjectType;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use stdClass;
|
||||
|
||||
final class TreeNodeTest extends TestCase
|
||||
{
|
||||
public function test_node_leaf_values_can_be_retrieved(): void
|
||||
{
|
||||
$type = FakeType::permissive();
|
||||
|
||||
$shell = Shell::root($type, 'some source value');
|
||||
$node = TreeNode::leaf($shell, 'some value')->node();
|
||||
|
||||
self::assertTrue($node->isRoot());
|
||||
self::assertSame('', $node->name());
|
||||
self::assertSame('', $node->path());
|
||||
self::assertSame((string)$type, $node->type());
|
||||
self::assertTrue($node->sourceFilled());
|
||||
self::assertSame('some source value', $node->sourceValue());
|
||||
self::assertTrue($node->isValid());
|
||||
self::assertSame('some value', $node->mappedValue());
|
||||
}
|
||||
|
||||
public function test_node_leaf_with_incorrect_value_throws_exception(): void
|
||||
{
|
||||
$type = new FakeType();
|
||||
|
||||
$this->expectException(InvalidNodeValue::class);
|
||||
$this->expectExceptionCode(1630678334);
|
||||
$this->expectExceptionMessage("Value 'foo' does not match type `$type`.");
|
||||
|
||||
FakeTreeNode::leaf($type, 'foo');
|
||||
}
|
||||
|
||||
public function test_node_branch_values_can_be_retrieved(): void
|
||||
{
|
||||
$typeChildA = FakeType::permissive();
|
||||
$typeChildB = FakeType::permissive();
|
||||
$attributesChildA = new FakeAttributes();
|
||||
$attributesChildB = new FakeAttributes();
|
||||
|
||||
$children = FakeTreeNode::branch([
|
||||
'foo' => ['type' => $typeChildA, 'value' => 'foo', 'attributes' => $attributesChildA],
|
||||
'bar' => ['type' => $typeChildB, 'value' => 'bar', 'attributes' => $attributesChildB],
|
||||
])->children();
|
||||
|
||||
self::assertSame('foo', $children['foo']->node()->name());
|
||||
self::assertSame('foo', $children['foo']->node()->path());
|
||||
self::assertFalse($children['foo']->node()->isRoot());
|
||||
self::assertSame('bar', $children['bar']->node()->name());
|
||||
self::assertSame('bar', $children['bar']->node()->path());
|
||||
self::assertFalse($children['bar']->node()->isRoot());
|
||||
}
|
||||
|
||||
public function test_node_branch_with_duplicated_child_name_throws_exception(): void
|
||||
{
|
||||
$this->expectException(DuplicatedNodeChild::class);
|
||||
$this->expectExceptionCode(1634045114);
|
||||
$this->expectExceptionMessage('The child `foo` is duplicated in the branch.');
|
||||
|
||||
FakeTreeNode::branch([
|
||||
['name' => 'foo'],
|
||||
['name' => 'foo'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_node_branch_with_incorrect_value_throws_exception(): void
|
||||
{
|
||||
$type = new FakeType();
|
||||
|
||||
$this->expectException(InvalidNodeValue::class);
|
||||
$this->expectExceptionCode(1630678334);
|
||||
$this->expectExceptionMessage("Value 'foo' does not match type `$type`.");
|
||||
|
||||
FakeTreeNode::branch([], $type, 'foo');
|
||||
}
|
||||
|
||||
public function test_node_error_values_can_be_retrieved(): void
|
||||
{
|
||||
$message = new FakeErrorMessage();
|
||||
$node = FakeTreeNode::error($message);
|
||||
|
||||
self::assertFalse($node->isValid());
|
||||
self::assertSame('some error message', (string)$node->node()->messages()[0]);
|
||||
}
|
||||
|
||||
public function test_get_value_from_invalid_node_throws_exception(): void
|
||||
{
|
||||
$node = FakeTreeNode::error();
|
||||
|
||||
$this->expectException(AssertionError::class);
|
||||
|
||||
$node->value();
|
||||
}
|
||||
|
||||
public function test_branch_node_with_invalid_child_is_invalid(): void
|
||||
{
|
||||
$node = FakeTreeNode::branch([
|
||||
'foo' => [],
|
||||
'bar' => ['message' => new FakeErrorMessage()],
|
||||
]);
|
||||
|
||||
self::assertFalse($node->isValid());
|
||||
}
|
||||
|
||||
public function test_node_with_value_returns_node_with_value(): void
|
||||
{
|
||||
$nodeA = FakeTreeNode::any();
|
||||
$nodeB = $nodeA->withValue('bar');
|
||||
|
||||
self::assertNotSame($nodeA, $nodeB);
|
||||
self::assertSame('bar', $nodeB->value());
|
||||
self::assertTrue($nodeB->isValid());
|
||||
}
|
||||
|
||||
public function test_node_with_invalid_value_returns_invalid_node(): void
|
||||
{
|
||||
$type = FakeType::accepting('foo');
|
||||
|
||||
$this->expectException(InvalidNodeValue::class);
|
||||
$this->expectExceptionCode(1630678334);
|
||||
$this->expectExceptionMessage("Value 1337 does not match type `$type`.");
|
||||
|
||||
FakeTreeNode::leaf($type, 'foo')->withValue(1337);
|
||||
}
|
||||
|
||||
public function test_node_with_invalid_value_for_object_type_returns_invalid_node(): void
|
||||
{
|
||||
$object = new stdClass();
|
||||
$type = FakeObjectType::accepting($object);
|
||||
|
||||
$this->expectException(InvalidNodeValue::class);
|
||||
$this->expectExceptionCode(1630678334);
|
||||
$this->expectExceptionMessage('Invalid value 1337.');
|
||||
|
||||
FakeTreeNode::leaf($type, $object)->withValue(1337);
|
||||
}
|
||||
|
||||
public function test_node_with_messages_returns_node_with_messages(): void
|
||||
{
|
||||
$messageA = new FakeMessage('some message A');
|
||||
$messageB = new FakeMessage('some message B');
|
||||
|
||||
$nodeA = FakeTreeNode::any();
|
||||
$nodeB = $nodeA->withMessage($messageA)->withMessage($messageB);
|
||||
|
||||
self::assertNotSame($nodeA, $nodeB);
|
||||
self::assertTrue($nodeB->isValid());
|
||||
self::assertSame('some message A', (string)$nodeB->node()->messages()[0]);
|
||||
self::assertSame('some message B', (string)$nodeB->node()->messages()[1]);
|
||||
}
|
||||
|
||||
public function test_node_with_error_message_returns_invalid_node(): void
|
||||
{
|
||||
$message = new FakeMessage();
|
||||
$errorMessage = new FakeErrorMessage();
|
||||
|
||||
$nodeA = FakeTreeNode::any();
|
||||
$nodeB = $nodeA->withMessage($message)->withMessage($errorMessage);
|
||||
|
||||
self::assertNotSame($nodeA, $nodeB);
|
||||
self::assertFalse($nodeB->isValid());
|
||||
self::assertSame('some message', (string)$nodeB->node()->messages()[0]);
|
||||
self::assertSame('some error message', (string)$nodeB->node()->messages()[1]);
|
||||
}
|
||||
}
|
@ -5,32 +5,26 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Tests\Unit\Mapper\Tree\Message\Formatter;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\Formatter\PlaceHolderMessageFormatter;
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\NodeMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\FakeShell;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeErrorMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeNodeMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\Formatter\FakeMessageFormatter;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class PlaceHolderMessageFormatterTest extends TestCase
|
||||
{
|
||||
public function test_format_message_replaces_placeholders_with_default_values(): void
|
||||
{
|
||||
$type = FakeType::permissive();
|
||||
$shell = FakeShell::any()->child('foo', $type)->withValue('some value');
|
||||
|
||||
$message = new NodeMessage($shell, new FakeMessage('some message'));
|
||||
$message = FakeNodeMessage::withMessage(new FakeMessage('some message'));
|
||||
$message = (new FakeMessageFormatter('%1$s / %2$s / %3$s / %4$s / %5$s'))->format($message);
|
||||
$message = (new PlaceHolderMessageFormatter())->format($message);
|
||||
|
||||
self::assertSame("some_code / some message / `$type` / foo / foo", (string)$message);
|
||||
self::assertSame("some_code / some message / `string` / nodeName / some.node.path", (string)$message);
|
||||
}
|
||||
|
||||
public function test_format_message_replaces_correct_original_value_if_throwable(): void
|
||||
public function test_format_message_replaces_correct_source_value_if_throwable(): void
|
||||
{
|
||||
$message = FakeNodeMessage::with(new FakeErrorMessage('some error message'));
|
||||
$message = FakeNodeMessage::withMessage(new FakeErrorMessage('some error message'));
|
||||
$message = (new FakeMessageFormatter('original: %2$s'))->format($message);
|
||||
$message = (new PlaceHolderMessageFormatter())->format($message);
|
||||
|
||||
@ -41,7 +35,7 @@ final class PlaceHolderMessageFormatterTest extends TestCase
|
||||
{
|
||||
$formatter = new PlaceHolderMessageFormatter('foo', 'bar');
|
||||
|
||||
$message = FakeNodeMessage::with(new FakeMessage('%1$s / %2$s'));
|
||||
$message = FakeNodeMessage::withMessage(new FakeMessage('%1$s / %2$s'));
|
||||
$message = $formatter->format($message);
|
||||
|
||||
self::assertSame('foo / bar', (string)$message);
|
||||
|
@ -20,7 +20,7 @@ final class TranslationMessageFormatterTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
$message = FakeNodeMessage::with(new FakeMessage('some key'));
|
||||
$message = FakeNodeMessage::withMessage(new FakeMessage('some key'));
|
||||
$message = $formatter->format($message);
|
||||
|
||||
self::assertSame('some message', (string)$message);
|
||||
@ -38,7 +38,7 @@ final class TranslationMessageFormatterTest extends TestCase
|
||||
'some other message'
|
||||
);
|
||||
|
||||
$message = FakeNodeMessage::with(new FakeMessage('some key'));
|
||||
$message = FakeNodeMessage::withMessage(new FakeMessage('some key'));
|
||||
$message = $formatter->format($message);
|
||||
|
||||
self::assertSame('some other message', (string)$message);
|
||||
@ -57,7 +57,7 @@ final class TranslationMessageFormatterTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
$message = FakeNodeMessage::with(new FakeMessage('some key'));
|
||||
$message = FakeNodeMessage::withMessage(new FakeMessage('some key'));
|
||||
$message = $formatter->format($message);
|
||||
|
||||
self::assertSame('some other message', (string)$message);
|
||||
@ -79,7 +79,7 @@ final class TranslationMessageFormatterTest extends TestCase
|
||||
],
|
||||
]);
|
||||
|
||||
$message = FakeNodeMessage::with(new FakeMessage('some other key'));
|
||||
$message = FakeNodeMessage::withMessage(new FakeMessage('some other key'));
|
||||
$message = $formatter->format($message);
|
||||
|
||||
self::assertSame('some other message', (string)$message);
|
||||
@ -94,7 +94,7 @@ final class TranslationMessageFormatterTest extends TestCase
|
||||
);
|
||||
|
||||
$originalMessage = new FakeTranslatableMessage('Value {value} is not accepted.', ['value' => 'foo']);
|
||||
$message = FakeNodeMessage::with($originalMessage);
|
||||
$message = FakeNodeMessage::withMessage($originalMessage);
|
||||
$message = $formatter->format($message);
|
||||
|
||||
self::assertSame('Value foo is not accepted!', (string)$message);
|
||||
|
@ -5,7 +5,8 @@ declare(strict_types=1);
|
||||
namespace CuyZ\Valinor\Tests\Unit\Mapper\Tree\Message;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\MessagesFlattener;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\FakeNode;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\FakeNode;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeErrorMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeMessage;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
@ -18,10 +19,20 @@ final class MessagesFlattenerTest extends TestCase
|
||||
$errorA = new FakeErrorMessage('some error message A');
|
||||
$errorB = new FakeErrorMessage('some error message B');
|
||||
|
||||
$node = FakeNode::branch([
|
||||
'foo' => ['message' => $messageA],
|
||||
'bar' => ['message' => $errorA],
|
||||
])->withMessage($errorB);
|
||||
$node = new Node(
|
||||
true,
|
||||
'nodeName',
|
||||
'some.node.path',
|
||||
'string',
|
||||
true,
|
||||
'some source value',
|
||||
'some value',
|
||||
[$errorB],
|
||||
[
|
||||
'foo' => FakeNode::withMessage($messageA),
|
||||
'bar' => FakeNode::withMessage($errorA),
|
||||
]
|
||||
);
|
||||
|
||||
$messages = [...(new MessagesFlattener($node))->errors()];
|
||||
|
||||
|
@ -6,14 +6,11 @@ namespace CuyZ\Valinor\Tests\Unit\Mapper\Tree\Message;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\Message;
|
||||
use CuyZ\Valinor\Mapper\Tree\Message\NodeMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Definition\FakeAttributes;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\FakeShell;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\FakeNode;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeErrorMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeNodeMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeTranslatableMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\Formatter\FakeMessageFormatter;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class NodeMessageTest extends TestCase
|
||||
@ -21,24 +18,27 @@ final class NodeMessageTest extends TestCase
|
||||
public function test_node_properties_can_be_accessed(): void
|
||||
{
|
||||
$originalMessage = new FakeMessage();
|
||||
$type = FakeType::permissive();
|
||||
$attributes = new FakeAttributes();
|
||||
|
||||
$shell = FakeShell::any()->child('foo', $type, $attributes)->withValue('some value');
|
||||
$message = new NodeMessage($shell, $originalMessage);
|
||||
$message = new NodeMessage(FakeNode::any(), $originalMessage);
|
||||
|
||||
self::assertSame('foo', $message->name());
|
||||
self::assertSame('foo', $message->path());
|
||||
self::assertSame('nodeName', $message->node()->name());
|
||||
self::assertSame('nodeName', $message->name());
|
||||
self::assertSame('some.node.path', $message->node()->path());
|
||||
self::assertSame('some.node.path', $message->path());
|
||||
self::assertSame('string', $message->node()->type());
|
||||
self::assertSame('string', $message->type());
|
||||
self::assertSame('some source value', $message->node()->sourceValue());
|
||||
self::assertSame('some value', $message->node()->mappedValue());
|
||||
self::assertSame('some value', $message->node()->value());
|
||||
self::assertSame('some value', $message->value());
|
||||
self::assertSame($type, $message->type());
|
||||
self::assertSame($attributes, $message->attributes());
|
||||
self::assertSame($originalMessage, $message->originalMessage());
|
||||
self::assertFalse($message->isError());
|
||||
}
|
||||
|
||||
public function test_message_is_error_if_original_message_is_throwable(): void
|
||||
{
|
||||
$originalMessage = new FakeErrorMessage();
|
||||
$message = new NodeMessage(FakeShell::any(), $originalMessage);
|
||||
$message = FakeNodeMessage::withMessage($originalMessage);
|
||||
|
||||
self::assertTrue($message->isError());
|
||||
self::assertSame('1652883436', $message->code());
|
||||
@ -48,34 +48,23 @@ final class NodeMessageTest extends TestCase
|
||||
public function test_parameters_are_replaced_in_body(): void
|
||||
{
|
||||
$originalMessage = new FakeTranslatableMessage('some original message', ['some_parameter' => 'some parameter value']);
|
||||
$type = FakeType::permissive();
|
||||
$shell = FakeShell::any()->child('foo', $type)->withValue('some value');
|
||||
|
||||
$message = new NodeMessage($shell, $originalMessage);
|
||||
$message = $message->withBody('{message_code} / {node_name} / {node_path} / {node_type} / {original_value} / {original_message} / {some_parameter}');
|
||||
$message = new NodeMessage(FakeNode::any(), $originalMessage);
|
||||
$message = $message->withBody('{message_code} / {node_name} / {node_path} / {node_type} / {original_value} / {source_value} / {original_message} / {some_parameter}');
|
||||
|
||||
self::assertSame("1652902453 / foo / foo / `$type` / 'some value' / some original message (toString) / some parameter value", (string)$message);
|
||||
self::assertSame("1652902453 / nodeName / some.node.path / `string` / 'some source value' / 'some source value' / some original message (toString) / some parameter value", (string)$message);
|
||||
}
|
||||
|
||||
public function test_replaces_correct_original_message_if_throwable(): void
|
||||
{
|
||||
$message = new NodeMessage(FakeShell::any(), new FakeErrorMessage('some error message'));
|
||||
$originalMessage = new FakeErrorMessage('some error message');
|
||||
|
||||
$message = FakeNodeMessage::withMessage($originalMessage);
|
||||
$message = $message->withBody('original: {original_message}');
|
||||
|
||||
self::assertSame('original: some error message', (string)$message);
|
||||
}
|
||||
|
||||
public function test_format_message_uses_formatter_to_replace_content(): void
|
||||
{
|
||||
$originalMessage = new FakeMessage('some message');
|
||||
|
||||
$message = new NodeMessage(FakeShell::any(), $originalMessage);
|
||||
$formattedMessage = (new FakeMessageFormatter())->format($message);
|
||||
|
||||
self::assertNotSame($message, $formattedMessage);
|
||||
self::assertSame('formatted: some message', (string)$formattedMessage);
|
||||
}
|
||||
|
||||
public function test_custom_body_returns_clone(): void
|
||||
{
|
||||
$messageA = FakeNodeMessage::any();
|
||||
@ -96,7 +85,7 @@ final class NodeMessageTest extends TestCase
|
||||
{
|
||||
$originalMessage = new FakeTranslatableMessage('un message: {value, spellout}', ['value' => '42']);
|
||||
|
||||
$message = new NodeMessage(FakeShell::any(), $originalMessage);
|
||||
$message = FakeNodeMessage::withMessage($originalMessage);
|
||||
$message = $message->withLocale('fr');
|
||||
|
||||
self::assertSame('un message: quarante-deux', (string)$message);
|
||||
@ -111,7 +100,7 @@ final class NodeMessageTest extends TestCase
|
||||
}
|
||||
};
|
||||
|
||||
$message = new NodeMessage(FakeShell::any(), $originalMessage);
|
||||
$message = FakeNodeMessage::withMessage($originalMessage);
|
||||
|
||||
self::assertSame('unknown', $message->code());
|
||||
}
|
||||
|
@ -4,177 +4,99 @@ declare(strict_types=1);
|
||||
|
||||
namespace CuyZ\Valinor\Tests\Unit\Mapper\Tree;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\CannotGetInvalidNodeValue;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\DuplicatedNodeChild;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidNodeValue;
|
||||
use CuyZ\Valinor\Tests\Fake\Definition\FakeAttributes;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\FakeNode;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidNodeHasNoMappedValue;
|
||||
use CuyZ\Valinor\Mapper\Tree\Exception\SourceValueWasNotFilled;
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\FakeNode;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeErrorMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Message\FakeMessage;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeObjectType;
|
||||
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use stdClass;
|
||||
|
||||
final class NodeTest extends TestCase
|
||||
{
|
||||
public function test_node_leaf_values_can_be_retrieved(): void
|
||||
public function test_properties_can_be_accessed(): void
|
||||
{
|
||||
$type = FakeType::permissive();
|
||||
$value = 'some node value';
|
||||
$message = new FakeMessage('some message');
|
||||
$child = FakeNode::any();
|
||||
|
||||
$node = FakeNode::leaf($type, $value);
|
||||
$node = new Node(
|
||||
true,
|
||||
'nodeName',
|
||||
'some.node.path',
|
||||
'string',
|
||||
true,
|
||||
'some source value',
|
||||
'some value',
|
||||
[$message],
|
||||
[$child]
|
||||
);
|
||||
|
||||
self::assertSame($type, $node->type());
|
||||
self::assertSame($value, $node->value());
|
||||
self::assertTrue($node->isRoot());
|
||||
self::assertTrue($node->isValid());
|
||||
self::assertSame(true, $node->isRoot());
|
||||
self::assertSame('nodeName', $node->name());
|
||||
self::assertSame('some.node.path', $node->path());
|
||||
self::assertSame('string', $node->type());
|
||||
self::assertSame('some source value', $node->sourceValue());
|
||||
self::assertSame('some value', $node->value());
|
||||
self::assertSame('some value', $node->mappedValue());
|
||||
self::assertSame(true, $node->isValid());
|
||||
self::assertSame('some message', (string)$node->messages()[0]);
|
||||
self::assertSame([$child], $node->children());
|
||||
}
|
||||
|
||||
public function test_node_leaf_with_incorrect_value_throws_exception(): void
|
||||
{
|
||||
$type = new FakeType();
|
||||
|
||||
$this->expectException(InvalidNodeValue::class);
|
||||
$this->expectExceptionCode(1630678334);
|
||||
$this->expectExceptionMessage("Value 'foo' does not match type `$type`.");
|
||||
|
||||
FakeNode::leaf($type, 'foo');
|
||||
}
|
||||
|
||||
public function test_node_branch_values_can_be_retrieved(): void
|
||||
{
|
||||
$typeChildA = FakeType::permissive();
|
||||
$typeChildB = FakeType::permissive();
|
||||
$attributesChildA = new FakeAttributes();
|
||||
$attributesChildB = new FakeAttributes();
|
||||
|
||||
$children = FakeNode::branch([
|
||||
'foo' => ['type' => $typeChildA, 'value' => 'foo', 'attributes' => $attributesChildA],
|
||||
'bar' => ['type' => $typeChildB, 'value' => 'bar', 'attributes' => $attributesChildB],
|
||||
])->children();
|
||||
|
||||
self::assertSame('foo', $children['foo']->name());
|
||||
self::assertSame('foo', $children['foo']->path());
|
||||
self::assertFalse($children['foo']->isRoot());
|
||||
self::assertSame($attributesChildA, $children['foo']->attributes());
|
||||
self::assertSame('bar', $children['bar']->name());
|
||||
self::assertSame('bar', $children['bar']->path());
|
||||
self::assertFalse($children['bar']->isRoot());
|
||||
self::assertSame($attributesChildB, $children['bar']->attributes());
|
||||
}
|
||||
|
||||
public function test_node_branch_with_duplicated_child_name_throws_exception(): void
|
||||
{
|
||||
$this->expectException(DuplicatedNodeChild::class);
|
||||
$this->expectExceptionCode(1634045114);
|
||||
$this->expectExceptionMessage('The child `foo` is duplicated in the branch.');
|
||||
|
||||
FakeNode::branch([
|
||||
['name' => 'foo'],
|
||||
['name' => 'foo'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_node_branch_with_incorrect_value_throws_exception(): void
|
||||
{
|
||||
$type = new FakeType();
|
||||
|
||||
$this->expectException(InvalidNodeValue::class);
|
||||
$this->expectExceptionCode(1630678334);
|
||||
$this->expectExceptionMessage("Value 'foo' does not match type `$type`.");
|
||||
|
||||
FakeNode::branch([], $type, 'foo');
|
||||
}
|
||||
|
||||
public function test_node_error_values_can_be_retrieved(): void
|
||||
public function test_error_message_makes_node_not_valid(): void
|
||||
{
|
||||
$message = new FakeErrorMessage();
|
||||
$node = FakeNode::error($message);
|
||||
|
||||
self::assertFalse($node->isValid());
|
||||
self::assertSame('some error message', (string)$node->messages()[0]);
|
||||
$node = new Node(
|
||||
true,
|
||||
'nodeName',
|
||||
'some.node.path',
|
||||
'string',
|
||||
true,
|
||||
'some source value',
|
||||
'some value',
|
||||
[$message],
|
||||
[]
|
||||
);
|
||||
|
||||
self::assertSame(false, $node->isValid());
|
||||
}
|
||||
|
||||
public function test_get_value_from_invalid_node_throws_exception(): void
|
||||
public function test_get_source_value_not_filled_throws_exception(): void
|
||||
{
|
||||
$node = FakeNode::error();
|
||||
$this->expectException(SourceValueWasNotFilled::class);
|
||||
$this->expectExceptionCode(1657466107);
|
||||
$this->expectExceptionMessage('Source was not filled at path `some.node.path`; use method `$node->sourceFilled()`.');
|
||||
|
||||
$this->expectException(CannotGetInvalidNodeValue::class);
|
||||
$this->expectExceptionCode(1630680246);
|
||||
$this->expectExceptionMessage('Trying to get value of an invalid node at path ``.');
|
||||
|
||||
$node->value();
|
||||
(new Node(
|
||||
true,
|
||||
'nodeName',
|
||||
'some.node.path',
|
||||
'string',
|
||||
false,
|
||||
null,
|
||||
'some value',
|
||||
[],
|
||||
[]
|
||||
))->sourceValue();
|
||||
}
|
||||
|
||||
public function test_branch_node_with_invalid_child_is_invalid(): void
|
||||
public function test_get_mapped_value_from_invalid_node_throws_exception(): void
|
||||
{
|
||||
$node = FakeNode::branch([
|
||||
'foo' => [],
|
||||
'bar' => ['message' => new FakeErrorMessage()],
|
||||
]);
|
||||
$this->expectException(InvalidNodeHasNoMappedValue::class);
|
||||
$this->expectExceptionCode(1657466305);
|
||||
$this->expectExceptionMessage('Cannot get mapped value for invalid node at path `some.node.path`; use method `$node->isValid()`.');
|
||||
|
||||
self::assertFalse($node->isValid());
|
||||
}
|
||||
|
||||
public function test_node_with_value_returns_node_with_value(): void
|
||||
{
|
||||
$nodeA = FakeNode::any();
|
||||
$nodeB = $nodeA->withValue('bar');
|
||||
|
||||
self::assertNotSame($nodeA, $nodeB);
|
||||
self::assertSame('bar', $nodeB->value());
|
||||
self::assertTrue($nodeB->isValid());
|
||||
}
|
||||
|
||||
public function test_node_with_invalid_value_returns_invalid_node(): void
|
||||
{
|
||||
$type = FakeType::accepting('foo');
|
||||
|
||||
$this->expectException(InvalidNodeValue::class);
|
||||
$this->expectExceptionCode(1630678334);
|
||||
$this->expectExceptionMessage("Value 1337 does not match type `$type`.");
|
||||
|
||||
FakeNode::leaf($type, 'foo')->withValue(1337);
|
||||
}
|
||||
|
||||
public function test_node_with_invalid_value_for_object_type_returns_invalid_node(): void
|
||||
{
|
||||
$object = new stdClass();
|
||||
$type = FakeObjectType::accepting($object);
|
||||
|
||||
$this->expectException(InvalidNodeValue::class);
|
||||
$this->expectExceptionCode(1630678334);
|
||||
$this->expectExceptionMessage('Invalid value 1337.');
|
||||
|
||||
FakeNode::leaf($type, $object)->withValue(1337);
|
||||
}
|
||||
|
||||
public function test_node_with_messages_returns_node_with_messages(): void
|
||||
{
|
||||
$messageA = new FakeMessage('some message A');
|
||||
$messageB = new FakeMessage('some message B');
|
||||
|
||||
$nodeA = FakeNode::any();
|
||||
$nodeB = $nodeA->withMessage($messageA)->withMessage($messageB);
|
||||
|
||||
self::assertNotSame($nodeA, $nodeB);
|
||||
self::assertTrue($nodeB->isValid());
|
||||
self::assertSame('some message A', (string)$nodeB->messages()[0]);
|
||||
self::assertSame('some message B', (string)$nodeB->messages()[1]);
|
||||
}
|
||||
|
||||
public function test_node_with_error_message_returns_invalid_node(): void
|
||||
{
|
||||
$message = new FakeMessage();
|
||||
$errorMessage = new FakeErrorMessage();
|
||||
|
||||
$nodeA = FakeNode::any();
|
||||
$nodeB = $nodeA->withMessage($message)->withMessage($errorMessage);
|
||||
|
||||
self::assertNotSame($nodeA, $nodeB);
|
||||
self::assertFalse($nodeB->isValid());
|
||||
self::assertSame('some message', (string)$nodeB->messages()[0]);
|
||||
self::assertSame('some error message', (string)$nodeB->messages()[1]);
|
||||
(new Node(
|
||||
true,
|
||||
'nodeName',
|
||||
'some.node.path',
|
||||
'string',
|
||||
true,
|
||||
'some source value',
|
||||
null,
|
||||
[new FakeErrorMessage()],
|
||||
[]
|
||||
))->mappedValue();
|
||||
}
|
||||
}
|
||||
|
@ -6,24 +6,26 @@ namespace CuyZ\Valinor\Tests\Unit\Mapper\Tree;
|
||||
|
||||
use CuyZ\Valinor\Mapper\Tree\Node;
|
||||
use CuyZ\Valinor\Mapper\Tree\NodeTraverser;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\FakeNode;
|
||||
use CuyZ\Valinor\Tests\Fake\Mapper\Tree\FakeNode;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class NodeTraverserTest extends TestCase
|
||||
{
|
||||
public function test_nodes_are_visited(): void
|
||||
{
|
||||
$node = FakeNode::branch([
|
||||
'foo' => [],
|
||||
'bar' => [],
|
||||
]);
|
||||
$children = [
|
||||
'foo' => FakeNode::any(),
|
||||
'bar' => FakeNode::any(),
|
||||
];
|
||||
|
||||
$node = FakeNode::branch($children);
|
||||
|
||||
$visited = [...(new NodeTraverser(
|
||||
fn (Node $node) => $node
|
||||
))->traverse($node)];
|
||||
|
||||
self::assertContains($node, $visited);
|
||||
self::assertContains($node->children()['foo'], $visited);
|
||||
self::assertContains($node->children()['bar'], $visited);
|
||||
self::assertContains($children['foo'], $visited);
|
||||
self::assertContains($children['bar'], $visited);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user