Skip to content

Commit

Permalink
make Shape accept mixed as input
Browse files Browse the repository at this point in the history
  • Loading branch information
Baptouuuu committed Mar 24, 2024
1 parent 705bc63 commit 660ea40
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 42 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
### Changed

- `Is::array()->and(Is::list())` has been shortened to `Is::list()`
- `Is::array()->and(Shape::of(...$args))` has been shortened to `Shape::of(...$args)`

## 1.3.0 - 2024-03-05

Expand Down
32 changes: 32 additions & 0 deletions proofs/shape.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Shape,
Is,
};
use Innmind\BlackBox\Set;

return static function() {
yield test(
Expand Down Expand Up @@ -175,4 +176,35 @@ static function($assert) {
);
},
);

yield proof(
'Shape validates non arrays',
given(
Set\Either::any(
Set\Strings::any(),
Set\Integers::any(),
Set\RealNumbers::any(),
Set\Elements::of(
true,
false,
new stdClass,
),
),
),
static function($assert, $value) {
$assert->same(
[['$', 'Value is not of type array']],
Shape::of('bar', Is::int())($value)
->match(
static fn() => null,
static fn($failures) => $failures
->map(static fn($failure) => [
$failure->path()->toString(),
$failure->message(),
])
->toList(),
),
);
},
);
};
6 changes: 2 additions & 4 deletions src/Is.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,10 @@ public static function list(Constraint $each = null): Constraint
* @psalm-pure
*
* @param non-empty-string $key
*
* @return Constraint<mixed, non-empty-array<non-empty-string, mixed>>
*/
public static function shape(string $key, Constraint $constraint): Constraint
public static function shape(string $key, Constraint $constraint): Shape
{
return self::array()->and(Shape::of($key, $constraint));
return Shape::of($key, $constraint);
}

public function and(Constraint $constraint): Constraint
Expand Down
84 changes: 46 additions & 38 deletions src/Shape.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
};

/**
* @implements Constraint<array, non-empty-array<non-empty-string, mixed>>
* @implements Constraint<mixed, non-empty-array<non-empty-string, mixed>>
* @psalm-immutable
*/
final class Shape implements Constraint
Expand All @@ -31,43 +31,7 @@ private function __construct(array $constraints, array $optional)

public function __invoke(mixed $value): Validation
{
$optional = new \stdClass;
/** @var Validation<Failure, non-empty-array<non-empty-string, mixed>> */
$validation = Validation::success([]);

foreach ($this->constraints as $key => $constraint) {
$keyValidation = Has::key($key);

if (\in_array($key, $this->optional, true)) {
/** @psalm-suppress MixedArgumentTypeCoercion */
$keyValidation = $keyValidation->or(Of::callable(
static fn() => Validation::success($optional),
));
}

$ofType = Of::callable(
static fn($value) => match ($value) {
$optional => Validation::success($optional),
default => $constraint($value)->mapFailures(
static fn($failure) => $failure->under($key),
),
},
);

$validation = $validation->and(
$keyValidation->and($ofType)($value),
static function($array, $value) use ($key, $optional) {
if ($value !== $optional) {
/** @psalm-suppress MixedAssignment */
$array[$key] = $value;
}

return $array;
},
);
}

return $validation;
return Is::array()($value)->flatMap($this->validate(...));
}

/**
Expand Down Expand Up @@ -126,4 +90,48 @@ public function asPredicate(): PredicateInterface
{
return Predicate::of($this);
}

/**
* @return Validation<Failure, non-empty-array<non-empty-string, mixed>>
*/
private function validate(array $value): Validation
{
$optional = new \stdClass;
/** @var Validation<Failure, non-empty-array<non-empty-string, mixed>> */
$validation = Validation::success([]);

foreach ($this->constraints as $key => $constraint) {
$keyValidation = Has::key($key);

if (\in_array($key, $this->optional, true)) {
/** @psalm-suppress MixedArgumentTypeCoercion */
$keyValidation = $keyValidation->or(Of::callable(
static fn() => Validation::success($optional),
));
}

$ofType = Of::callable(
static fn($value) => match ($value) {
$optional => Validation::success($optional),
default => $constraint($value)->mapFailures(
static fn($failure) => $failure->under($key),
),
},
);

$validation = $validation->and(
$keyValidation->and($ofType)($value),
static function($array, $value) use ($key, $optional) {
if ($value !== $optional) {
/** @psalm-suppress MixedAssignment */
$array[$key] = $value;
}

return $array;
},
);
}

return $validation;
}
}

0 comments on commit 660ea40

Please sign in to comment.