Skip to content

Commit

Permalink
misc: move setting values in shell
Browse files Browse the repository at this point in the history
This, for instance, allows to more easily check for unexpected keys in
the source.
  • Loading branch information
romm committed Sep 2, 2024
1 parent ba22b52 commit 84b1ffb
Show file tree
Hide file tree
Showing 26 changed files with 128 additions and 162 deletions.
21 changes: 10 additions & 11 deletions src/Library/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,36 +86,37 @@ public function __construct(Settings $settings)
$this->factories = [
TreeMapper::class => fn () => new TypeTreeMapper(
$this->get(TypeParser::class),
$this->get(RootNodeBuilder::class)
$this->get(RootNodeBuilder::class),
$settings,
),

ArgumentsMapper::class => fn () => new TypeArgumentsMapper(
$this->get(FunctionDefinitionRepository::class),
$this->get(RootNodeBuilder::class)
$this->get(RootNodeBuilder::class),
$settings,
),

RootNodeBuilder::class => fn () => new RootNodeBuilder(
$this->get(NodeBuilder::class)
),

NodeBuilder::class => function () use ($settings) {
$listNodeBuilder = new ListNodeBuilder($settings->enableFlexibleCasting);
$arrayNodeBuilder = new ArrayNodeBuilder($settings->enableFlexibleCasting);
$listNodeBuilder = new ListNodeBuilder();
$arrayNodeBuilder = new ArrayNodeBuilder();

$builder = new CasterNodeBuilder([
ListType::class => $listNodeBuilder,
NonEmptyListType::class => $listNodeBuilder,
ArrayType::class => $arrayNodeBuilder,
NonEmptyArrayType::class => $arrayNodeBuilder,
IterableType::class => $arrayNodeBuilder,
ShapedArrayType::class => new ShapedArrayNodeBuilder($settings->allowSuperfluousKeys),
ScalarType::class => new ScalarNodeBuilder($settings->enableFlexibleCasting),
ShapedArrayType::class => new ShapedArrayNodeBuilder(),
ScalarType::class => new ScalarNodeBuilder(),
NullType::class => new NullNodeBuilder(),
ObjectType::class => new ObjectNodeBuilder(
$this->get(ClassDefinitionRepository::class),
$this->get(ObjectBuilderFactory::class),
$this->get(FilteredObjectNodeBuilder::class),
$settings->enableFlexibleCasting,
),
]);

Expand All @@ -131,8 +132,6 @@ public function __construct(Settings $settings)
$this->get(FunctionDefinitionRepository::class),
$settings->customConstructors
),
$settings->enableFlexibleCasting,
$settings->allowSuperfluousKeys,
);

$builder = new CasterProxyNodeBuilder($builder);
Expand All @@ -148,12 +147,12 @@ public function __construct(Settings $settings)
);
}

$builder = new StrictNodeBuilder($builder, $settings->allowPermissiveTypes, $settings->enableFlexibleCasting);
$builder = new StrictNodeBuilder($builder);

return new ErrorCatcherNodeBuilder($builder, $settings->exceptionFilter);
},

FilteredObjectNodeBuilder::class => fn () => new FilteredObjectNodeBuilder($settings->allowSuperfluousKeys),
FilteredObjectNodeBuilder::class => fn () => new FilteredObjectNodeBuilder(),

ObjectImplementations::class => fn () => new ObjectImplementations(
new FunctionsContainer(
Expand Down
33 changes: 12 additions & 21 deletions src/Mapper/Object/ArgumentsValues.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
namespace CuyZ\Valinor\Mapper\Object;

use CuyZ\Valinor\Mapper\Object\Exception\InvalidSource;
use CuyZ\Valinor\Mapper\Tree\Shell;
use CuyZ\Valinor\Type\CompositeTraversableType;
use CuyZ\Valinor\Type\Types\ArrayKeyType;
use IteratorAggregate;
use Traversable;

use function array_filter;
use function array_key_exists;
use function count;
use function is_array;
Expand All @@ -36,22 +36,22 @@ private function __construct(Arguments $arguments)
$this->arguments = $arguments;
}

public static function forInterface(Arguments $arguments, mixed $value, bool $allowSuperfluousKeys): self
public static function forInterface(Arguments $arguments, Shell $shell): self
{
$self = new self($arguments);
$self->forInterface = true;

if (count($arguments) > 0) {
$self = $self->transform($value, $allowSuperfluousKeys);
$self = $self->transform($shell);
}

return $self;
}

public static function forClass(Arguments $arguments, mixed $value, bool $allowSuperfluousKeys): self
public static function forClass(Arguments $arguments, Shell $shell): self
{
$self = new self($arguments);
$self = $self->transform($value, $allowSuperfluousKeys);
$self = $self->transform($shell);

return $self;
}
Expand All @@ -66,33 +66,22 @@ public function getValue(string $name): mixed
return $this->value[$name];
}

/**
* @return array<string>
*/
public function superfluousKeys(): array
{
return array_filter(
array_keys($this->value),
fn ($key) => ! $this->arguments->has((string)$key)
);
}

public function hadSingleArgument(): bool
{
return $this->hadSingleArgument;
}

private function transform(mixed $value, bool $allowSuperfluousKeys): self
private function transform(Shell $shell): self
{
$clone = clone $this;

$transformedValue = $this->transformValueForSingleArgument($value, $allowSuperfluousKeys);
$transformedValue = $this->transformValueForSingleArgument($shell);

if (! is_array($transformedValue)) {
throw new InvalidSource($transformedValue, $this->arguments);
}

if ($transformedValue !== $value) {
if ($transformedValue !== $shell->value()) {
$clone->hadSingleArgument = true;
}

Expand All @@ -109,8 +98,10 @@ private function transform(mixed $value, bool $allowSuperfluousKeys): self
return $clone;
}

private function transformValueForSingleArgument(mixed $value, bool $allowSuperfluousKeys): mixed
private function transformValueForSingleArgument(Shell $shell): mixed
{
$value = $shell->value();

if (count($this->arguments) !== 1) {
return $value;
}
Expand All @@ -122,7 +113,7 @@ private function transformValueForSingleArgument(mixed $value, bool $allowSuperf
&& $type->keyType() !== ArrayKeyType::integer();

if (is_array($value) && array_key_exists($name, $value)) {
if ($this->forInterface || ! $isTraversableAndAllowsStringKeys || $allowSuperfluousKeys || count($value) === 1) {
if ($this->forInterface || ! $isTraversableAndAllowsStringKeys || $shell->allowSuperfluousKeys() || count($value) === 1) {
return $value;
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/Mapper/Tree/Builder/ArrayNodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,14 @@
/** @internal */
final class ArrayNodeBuilder implements NodeBuilder
{
public function __construct(private bool $enableFlexibleCasting) {}

public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
{
$type = $shell->type();
$value = $shell->value();

assert($type instanceof ArrayType || $type instanceof NonEmptyArrayType || $type instanceof IterableType);

if ($this->enableFlexibleCasting && $value === null) {
if ($shell->enableFlexibleCasting() && $value === null) {
return TreeNode::branch($shell, [], []);
}

Expand Down
15 changes: 2 additions & 13 deletions src/Mapper/Tree/Builder/FilteredObjectNodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,22 @@

use CuyZ\Valinor\Mapper\Object\ArgumentsValues;
use CuyZ\Valinor\Mapper\Object\ObjectBuilder;
use CuyZ\Valinor\Mapper\Tree\Exception\UnexpectedArrayKeysForClass;
use CuyZ\Valinor\Mapper\Tree\Shell;

use function count;

/** @internal */
final class FilteredObjectNodeBuilder
{
public function __construct(private bool $allowSuperfluousKeys) {}

public function build(ObjectBuilder $builder, Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
{
$arguments = ArgumentsValues::forClass($builder->describeArguments(), $shell->value(), $this->allowSuperfluousKeys);
$arguments = ArgumentsValues::forClass($builder->describeArguments(), $shell);

$children = $this->children($shell, $arguments, $rootBuilder);

$object = $this->buildObject($builder, $children);

$node = $arguments->hadSingleArgument()
return $arguments->hadSingleArgument()
? TreeNode::flattenedBranch($shell, $object, $children[0])
: TreeNode::branch($shell, $object, $children);

if (! $this->allowSuperfluousKeys && count($arguments->superfluousKeys()) > 0) {
$node = $node->withMessage(new UnexpectedArrayKeysForClass($arguments));
}

return $node;
}

/**
Expand Down
6 changes: 2 additions & 4 deletions src/Mapper/Tree/Builder/InterfaceNodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ public function __construct(
private ObjectBuilderFactory $objectBuilderFactory,
private FilteredObjectNodeBuilder $filteredObjectNodeBuilder,
private FunctionsContainer $constructors,
private bool $enableFlexibleCasting,
private bool $allowSuperfluousKeys,
) {}

public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
Expand All @@ -50,7 +48,7 @@ public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
return $this->delegate->build($shell, $rootBuilder);
}

if ($this->enableFlexibleCasting && $shell->value() === null) {
if ($shell->enableFlexibleCasting() && $shell->value() === null) {
$shell = $shell->withValue([]);
}

Expand Down Expand Up @@ -140,7 +138,7 @@ private function transformSourceForClass(Shell $shell, Arguments $interfaceArgum
*/
private function children(Shell $shell, Arguments $arguments, RootNodeBuilder $rootBuilder): array
{
$arguments = ArgumentsValues::forInterface($arguments, $shell->value(), $this->allowSuperfluousKeys);
$arguments = ArgumentsValues::forInterface($arguments, $shell);

$children = [];

Expand Down
6 changes: 2 additions & 4 deletions src/Mapper/Tree/Builder/ListNodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,14 @@
/** @internal */
final class ListNodeBuilder implements NodeBuilder
{
public function __construct(private bool $enableFlexibleCasting) {}

public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
{
$type = $shell->type();
$value = $shell->value();

assert($type instanceof ListType || $type instanceof NonEmptyListType);

if ($this->enableFlexibleCasting && $value === null) {
if ($shell->enableFlexibleCasting() && $value === null) {
return TreeNode::branch($shell, [], []);
}

Expand All @@ -53,7 +51,7 @@ private function children(CompositeTraversableType $type, Shell $shell, RootNode
$children = [];

foreach ($values as $key => $value) {
if ($this->enableFlexibleCasting || $key === $expected) {
if ($shell->enableFlexibleCasting() || $key === $expected) {
$child = $shell->child((string)$expected, $subType);
$children[$expected] = $rootBuilder->build($child->withValue($value));
} else {
Expand Down
3 changes: 1 addition & 2 deletions src/Mapper/Tree/Builder/ObjectNodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ public function __construct(
private ClassDefinitionRepository $classDefinitionRepository,
private ObjectBuilderFactory $objectBuilderFactory,
private FilteredObjectNodeBuilder $filteredObjectNodeBuilder,
private bool $enableFlexibleCasting,
) {}

public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
Expand All @@ -30,7 +29,7 @@ public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
// @infection-ignore-all
assert($type instanceof ObjectType);

if ($this->enableFlexibleCasting && $shell->value() === null) {
if ($shell->enableFlexibleCasting() && $shell->value() === null) {
$shell = $shell->withValue([]);
}

Expand Down
4 changes: 1 addition & 3 deletions src/Mapper/Tree/Builder/ScalarNodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@
/** @internal */
final class ScalarNodeBuilder implements NodeBuilder
{
public function __construct(private bool $enableFlexibleCasting) {}

public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
{
$type = $shell->type();
$value = $shell->value();

assert($type instanceof ScalarType);

if (! $this->enableFlexibleCasting || ! $type->canCast($value)) {
if (! $shell->enableFlexibleCasting() || ! $type->canCast($value)) {
throw $type->errorMessage();
}

Expand Down
12 changes: 1 addition & 11 deletions src/Mapper/Tree/Builder/ShapedArrayNodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,16 @@
namespace CuyZ\Valinor\Mapper\Tree\Builder;

use CuyZ\Valinor\Mapper\Tree\Exception\SourceMustBeIterable;
use CuyZ\Valinor\Mapper\Tree\Exception\UnexpectedShapedArrayKeys;
use CuyZ\Valinor\Mapper\Tree\Shell;
use CuyZ\Valinor\Type\Types\ShapedArrayType;

use function array_key_exists;
use function assert;
use function count;
use function is_array;

/** @internal */
final class ShapedArrayNodeBuilder implements NodeBuilder
{
public function __construct(private bool $allowSuperfluousKeys) {}

public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
{
$type = $shell->type();
Expand All @@ -34,13 +30,7 @@ public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode

$array = $this->buildArray($children);

$node = TreeNode::branch($shell, $array, $children);

if (! $this->allowSuperfluousKeys && count($value) > count($children)) {
$node = $node->withMessage(new UnexpectedShapedArrayKeys($value, $children));
}

return $node;
return TreeNode::branch($shell, $array, $children);
}

/**
Expand Down
6 changes: 2 additions & 4 deletions src/Mapper/Tree/Builder/StrictNodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,18 @@ final class StrictNodeBuilder implements NodeBuilder
{
public function __construct(
private NodeBuilder $delegate,
private bool $allowPermissiveTypes,
private bool $enableFlexibleCasting
) {}

public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
{
$type = $shell->type();

if (! $this->allowPermissiveTypes) {
if (! $shell->allowPermissiveTypes()) {
TypeHelper::checkPermissiveType($type);
}

if (! $shell->hasValue()) {
if ($this->enableFlexibleCasting) {
if ($shell->enableFlexibleCasting()) {
return $this->delegate->build($shell->withValue(null), $rootBuilder);
}

Expand Down
Loading

0 comments on commit 84b1ffb

Please sign in to comment.