Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
272 / 272
99.34% covered (success)
99.34%
151 / 152
65.22% covered (warning)
65.22%
75 / 115
96.77% covered (success)
96.77%
30 / 31
CRAP
0.00% covered (danger)
0.00%
0 / 1
ValidationRun
100.00% covered (success)
100.00%
272 / 272
99.34% covered (success)
99.34%
151 / 152
65.22% covered (warning)
65.22%
75 / 115
100.00% covered (success)
100.00%
31 / 31
372.90
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validate
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
11 / 11
25.00% covered (danger)
25.00%
4 / 16
100.00% covered (success)
100.00%
1 / 1
15.55
 prepareData
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
4 / 4
66.67% covered (warning)
66.67%
2 / 3
100.00% covered (success)
100.00%
1 / 1
2.15
 validateField
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
14 / 14
80.00% covered (warning)
80.00%
8 / 10
100.00% covered (success)
100.00%
1 / 1
7.39
 finalizeValues
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
6 / 6
50.00% covered (danger)
50.00%
2 / 4
100.00% covered (success)
100.00%
1 / 1
4.12
 finalizeItem
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
6 / 6
75.00% covered (warning)
75.00%
3 / 4
100.00% covered (success)
100.00%
1 / 1
3.14
 extractValues
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
6 / 6
50.00% covered (danger)
50.00%
2 / 4
100.00% covered (success)
100.00%
1 / 1
4.12
 readFromData
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
9 / 9
50.00% covered (danger)
50.00%
3 / 6
100.00% covered (success)
100.00%
1 / 1
6.00
 readFieldValue
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 readEmptyValue
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 readDefaultValue
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addReadErrors
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
5 / 5
75.00% covered (warning)
75.00%
3 / 4
100.00% covered (success)
100.00%
1 / 1
3.14
 readExtraValue
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 readKnownValue
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
5
 formatExtraFailure
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 formatNullFailure
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 formatMissingFailure
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 formatCoercionFailure
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 formatShapeFailure
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 formatFailure
100.00% covered (success)
100.00%
16 / 16
85.71% covered (warning)
85.71%
6 / 7
50.00% covered (danger)
50.00%
2 / 4
100.00% covered (success)
100.00%
1 / 1
4.12
 toSubValues
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 review
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
4 / 4
66.67% covered (warning)
66.67%
2 / 3
100.00% covered (success)
100.00%
1 / 1
2.15
 fillMissingFromFields
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
14 / 14
58.33% covered (warning)
58.33%
7 / 12
100.00% covered (success)
100.00%
1 / 1
10.54
 readValues
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
8 / 8
40.00% covered (danger)
40.00%
2 / 5
100.00% covered (success)
100.00%
1 / 1
7.46
 validateItem
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
9 / 9
66.67% covered (warning)
66.67%
4 / 6
100.00% covered (success)
100.00%
1 / 1
4.59
 getValues
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 blockRules
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 rulesBlocked
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 path
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 pathKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isRawEmptyValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
5 / 5
25.00% covered (danger)
25.00%
1 / 4
100.00% covered (success)
100.00%
1 / 1
6.80
1<?php
2
3declare(strict_types=1);
4
5namespace Celema\Sire;
6
7use Celema\Sire\Contract\Value;
8use ValueError;
9
10/** @internal */
11final class ValidationRun
12{
13    private ErrorBag $errors;
14    /** @var array<string, true> */
15    private array $ruleBlockedPaths = [];
16
17    /** @param array<array-key, mixed> $data */
18    public function __construct(
19        private readonly ShapeDefinition $shape,
20        private readonly array $data,
21    ) {
22        $this->errors = new ErrorBag();
23    }
24
25    public function validate(): Result
26    {
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
32            foreach ($values as $listIndex => $subValues) {
33                /** @var array<string, Value> $subValues */
34                $validatedValues[] = $this->validateItem($subValues, $listIndex);
35            }
36        } else {
37            /** @var array<string, Value> $values */
38            $validatedValues = $this->validateItem($values);
39        }
40
41        if (!$this->errors->hasErrors()) {
42            $validatedValues = $this->finalizeValues($validatedValues);
43        }
44
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
48            $this->review($extractedValues);
49        }
50
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
56
57    /**
58     * @param array<array-key, mixed> $data
59     * @return array<array-key, mixed>
60     */
61    private function prepareData(array $data): array
62    {
63        foreach ($this->shape->prepareCallbacks as $prepare) {
64            $data = $prepare($data);
65        }
66
67        return $data;
68    }
69
70    private function validateField(
71        Field $definition,
72        Value $value,
73        string $ruleDefinition,
74        string|int|null $listIndex,
75    ): void {
76        $parsedRule = $this->shape->ruleParser->parse($ruleDefinition);
77        $ruleName = $parsedRule['name'];
78        $ruleArgs = $parsedRule['args'];
79
80        $rule = $this->shape->rules->get($ruleName);
81
82        if ($rule === null) {
83            throw new ValueError(
84                sprintf('Unknown rule "%s" in field "%s"', $ruleName, $definition->field),
85            );
86        }
87
88        if ($this->rulesBlocked($definition->field, $listIndex)) {
89            return;
90        }
91
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
93            return;
94        }
95
96        $validation = $rule->validate($value, ...$ruleArgs);
97
98        if ($validation->failure !== null) {
99            $this->errors->add(
100                self::path($definition->field, $listIndex),
101                $this->formatFailure(
102                    $validation->failure,
103                    $definition->name(),
104                    $definition->field,
105                    $value->pristine,
106                    'rule.' . $ruleName,
107                    $rule->message,
108                    $rule instanceof Contract\NamedArguments
109                        ? Arguments::named($ruleArgs, $rule)
110                        : $ruleArgs,
111                    $definition->messageOverrides(),
112                ),
113            );
114        }
115    }
116
117    /**
118     * @param array<string, Value>|list<array<string, Value>> $validatedValues
119     * @return array<string, Value>|list<array<string, Value>>
120     */
121    private function finalizeValues(array $validatedValues): array
122    {
123        if ($this->shape->list) {
124            $values = [];
125
126            foreach ($validatedValues as $item) {
127                /** @var array<string, Value> $item */
128                $values[] = $this->finalizeItem($item);
129            }
130
131            return $values;
132        }
133
134        /** @var array<string, Value> $validatedValues */
135        return $this->finalizeItem($validatedValues);
136    }
137
138    /**
139     * @param array<string, Value> $values
140     * @return array<string, Value>
141     */
142    private function finalizeItem(array $values): array
143    {
144        $itemValues = $this->getValues($values);
145
146        foreach ($this->shape->fields as $field => $definition) {
147            if (!array_key_exists($field, $values)) {
148                continue;
149            }
150
151            $value = $values[$field];
152            $values[$field] = new \Celema\Sire\Value(
153                $definition->applyFinalization($value->value, $itemValues),
154                $value->pristine,
155                $value->empty,
156            );
157        }
158
159        return $values;
160    }
161
162    /**
163     * @param array<string, Value>|list<array<string, Value>> $validatedValues
164     * @return array<string, mixed>|list<array<string, mixed>>
165     */
166    private function extractValues(array $validatedValues): array
167    {
168        if ($this->shape->list) {
169            $values = [];
170
171            foreach ($validatedValues as $item) {
172                /** @var array<string, Value> $item */
173                $values[] = $this->getValues($item);
174            }
175
176            return $values;
177        }
178
179        /** @var array<string, Value> $validatedValues */
180        return $this->getValues($validatedValues);
181    }
182
183    /**
184     * @param array<array-key, mixed> $data
185     * @return array<string, Value>
186     */
187    private function readFromData(array $data, string|int|null $listIndex = null): array
188    {
189        $values = [];
190
191        /** @var mixed $value */
192        foreach ($data as $field => $value) {
193            $field = (string) $field;
194            $definition = $this->shape->fields[$field] ?? null;
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
198
199            if ($value !== null) {
200                $values[$field] = $value;
201            }
202        }
203
204        return $values;
205    }
206
207    /** @param array<array-key, mixed> $data */
208    private function readFieldValue(
209        string $field,
210        Field $definition,
211        mixed $value,
212        array $data,
213        string|int|null $listIndex,
214    ): ?Value {
215        /** @var mixed $value */
216        $value = $definition->applyPreparation($value, $data);
217
218        if ($definition->isBlank($value)) {
219            return $this->readEmptyValue($field, $definition, $data, $listIndex);
220        }
221
222        $read = $this->readKnownValue($definition, $value);
223        $this->addReadErrors($field, $read, $listIndex);
224
225        return $read->value;
226    }
227
228    /** @param array<array-key, mixed> $data */
229    private function readEmptyValue(
230        string $field,
231        Field $definition,
232        array $data,
233        string|int|null $listIndex,
234    ): ?Value {
235        if ($definition->hasDefault()) {
236            return $this->readDefaultValue($field, $definition, $data, $listIndex);
237        }
238
239        if ($definition->isOptional()) {
240            return null;
241        }
242
243        $this->errors->add(
244            self::path($field, $listIndex),
245            $this->formatMissingFailure($definition),
246        );
247
248        return null;
249    }
250
251    /** @param array<array-key, mixed> $data */
252    private function readDefaultValue(
253        string $field,
254        Field $definition,
255        array $data,
256        string|int|null $listIndex,
257    ): Value {
258        $read = $this->readKnownValue(
259            $definition,
260            $definition->applyPreparation($definition->defaultValue(), $data),
261        );
262        $this->addReadErrors($field, $read, $listIndex);
263
264        return $read->value;
265    }
266
267    private function addReadErrors(string $field, ReadValue $read, string|int|null $listIndex): void
268    {
269        if ($read->nestedResult !== null) {
270            $this->errors->addNested(self::path($field, $listIndex), $read->nestedResult);
271            $this->blockRules($field, $listIndex);
272        }
273
274        if ($read->issue !== null) {
275            $this->errors->add(self::path($field, $listIndex), $read->issue);
276            $this->blockRules($field, $listIndex);
277        }
278    }
279
280    private function readExtraValue(string $field, mixed $value, string|int|null $listIndex): ?Value
281    {
282        if ($this->shape->extra === Extra::Allow) {
283            return new \Celema\Sire\Value($value, $value, self::isRawEmptyValue($value));
284        }
285
286        if ($this->shape->extra === Extra::Forbid) {
287            $this->errors->add(
288                self::path($field, $listIndex),
289                $this->formatExtraFailure($field, $value),
290            );
291        }
292
293        return null;
294    }
295
296    private function readKnownValue(Field $definition, mixed $value): ReadValue
297    {
298        if ($value === null) {
299            return new ReadValue(
300                new \Celema\Sire\Value(null, null, true),
301                $definition->isNullable() ? null : $this->formatNullFailure($definition),
302            );
303        }
304
305        if ($definition->type instanceof Contract\Validator) {
306            return $this->toSubValues($value, $definition, $definition->type);
307        }
308
309        $coercer = $this->shape->coercers->get($definition->type);
310
311        if ($coercer === null) {
312            throw new ValueError('Wrong shape type');
313        }
314
315        $coercion = $coercer->coerce(
316            $value,
317            $definition->coercionMode($this->shape->coercionMode),
318        );
319
320        return new ReadValue(
321            new \Celema\Sire\Value($coercion->value, $coercion->pristine, $coercion->empty),
322            $this->formatCoercionFailure($coercion, $definition, $coercer),
323        );
324    }
325
326    private function formatExtraFailure(string $field, mixed $value): Issue
327    {
328        return $this->formatFailure(
329            Failure::invalid(),
330            $field,
331            $field,
332            $value,
333            'extra',
334            Messages::EXTRA,
335        );
336    }
337
338    private function formatNullFailure(Field $definition): Issue
339    {
340        return $this->formatFailure(
341            Failure::key('null'),
342            $definition->name(),
343            $definition->field,
344            null,
345            'null',
346            Messages::NULL,
347            messages: $definition->messageOverrides(),
348        );
349    }
350
351    private function formatMissingFailure(Field $definition): Issue
352    {
353        return $this->formatFailure(
354            Failure::key('missing'),
355            $definition->name(),
356            $definition->field,
357            null,
358            'missing',
359            Messages::MISSING,
360            messages: $definition->messageOverrides(),
361        );
362    }
363
364    private function formatCoercionFailure(
365        Contract\Coercion $coercion,
366        Field $definition,
367        Contract\Coercer $coercer,
368    ): ?Issue {
369        if ($coercion->failure === null) {
370            return null;
371        }
372
373        return $this->formatFailure(
374            $coercion->failure,
375            $definition->name(),
376            $definition->field,
377            $coercion->pristine,
378            'type.' . $definition->type(),
379            $coercer->message,
380            messages: $definition->messageOverrides(),
381        );
382    }
383
384    private function formatShapeFailure(Field $definition, mixed $value): Issue
385    {
386        return $this->formatFailure(
387            Failure::invalid(),
388            $definition->name(),
389            $definition->field,
390            $value,
391            'type.shape',
392            Messages::SHAPE,
393            messages: $definition->messageOverrides(),
394        );
395    }
396
397    /**
398     * @param array<array-key, mixed> $args
399     * @param array<string, string> $messages
400     */
401    private function formatFailure(
402        Failure $failure,
403        string $label,
404        string $field,
405        mixed $pristine,
406        ?string $defaultKey,
407        string $fallback,
408        array $args = [],
409        array $messages = [],
410    ): Issue {
411        $args = $failure->args === [] ? $args : $failure->args;
412
413        return new Issue(
414            [],
415            $failure->key !== '' ? $failure->key : $defaultKey ?? 'invalid',
416            $this->shape->messageFormatter->format(
417                $failure,
418                $label,
419                $field,
420                $pristine,
421                $defaultKey,
422                $fallback,
423                $args,
424                $messages,
425            ),
426            Arguments::keyed($args),
427        );
428    }
429
430    private function toSubValues(
431        mixed $pristine,
432        Field $definition,
433        Contract\Validator $shape,
434    ): ReadValue {
435        if (!is_array($pristine)) {
436            return new ReadValue(
437                new \Celema\Sire\Value($pristine, $pristine, self::isRawEmptyValue($pristine)),
438                $this->formatShapeFailure($definition, $pristine),
439            );
440        }
441
442        $result = $shape->validate($pristine);
443
444        if ($result->valid()) {
445            $values = $result->values();
446
447            return new ReadValue(new \Celema\Sire\Value($values, $pristine, $values === []));
448        }
449
450        return new ReadValue(
451            new \Celema\Sire\Value($pristine, $pristine, self::isRawEmptyValue($pristine)),
452            nestedResult: $result,
453        );
454    }
455
456    /** @param array<string, mixed>|list<array<string, mixed>> $values */
457    private function review(array $values): void
458    {
459        $context = new Review(
460            $this->errors,
461            $values,
462            $this->shape->list,
463        );
464
465        foreach ($this->shape->reviewCallbacks as $review) {
466            $review($context);
467        }
468    }
469
470    /**
471     * @param array<string, Value> $values
472     * @param array<array-key, mixed> $data
473     * @return array<string, Value>
474     */
475    private function fillMissingFromFields(
476        array $values,
477        array $data,
478        string|int|null $listIndex = null,
479    ): array {
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
500                self::path($field, $listIndex),
501                $this->formatMissingFailure($definition),
502            );
503        }
504
505        return $values;
506    }
507
508    /**
509     * @param array<array-key, mixed> $data
510     * @return array<string, Value>|list<array<string, Value>>
511     */
512    private function readValues(array $data): array
513    {
514        if ($this->shape->list) {
515            $values = [];
516
517            foreach ($data as $listIndex => $item) {
518                if (!is_array($item)) {
519                    $this->errors->add(
520                        [$listIndex],
521                        new Issue([], 'type.shape', 'Item must be an array'),
522                    );
523                    $values[] = [];
524
525                    continue;
526                }
527
528                $subValues = $this->readFromData($item, $listIndex);
529                $values[] = $this->fillMissingFromFields($subValues, $item, $listIndex);
530            }
531
532            return $values;
533        }
534
535        $values = $this->readFromData($data);
536
537        return $this->fillMissingFromFields($values, $data);
538    }
539
540    /**
541     * @param array<string, Value> $values
542     * @return array<string, Value>
543     */
544    private function validateItem(array $values, string|int|null $listIndex = null): array
545    {
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
548                continue;
549            }
550
551            foreach ($definition->rules as $rule) {
552                $this->validateField(
553                    $definition,
554                    $values[$field],
555                    $rule,
556                    $listIndex,
557                );
558            }
559        }
560
561        return $values;
562    }
563
564    /**
565     * @param array<string, Value> $values
566     * @return array<string, mixed>
567     */
568    private function getValues(array $values): array
569    {
570        return array_map(
571            static fn(Value $item): mixed => $item->value,
572            $values,
573        );
574    }
575
576    private function blockRules(string $field, string|int|null $listIndex): void
577    {
578        $this->ruleBlockedPaths[self::pathKey(self::path($field, $listIndex))] = true;
579    }
580
581    private function rulesBlocked(string $field, string|int|null $listIndex): bool
582    {
583        return array_key_exists(
584            self::pathKey(self::path($field, $listIndex)),
585            $this->ruleBlockedPaths,
586        );
587    }
588
589    /** @return list<string|int> */
590    private static function path(string $field, string|int|null $listIndex): array
591    {
592        if ($listIndex === null) {
593            return [$field];
594        }
595
596        return [$listIndex, $field];
597    }
598
599    /** @param list<string|int> $path */
600    private static function pathKey(array $path): string
601    {
602        return serialize($path);
603    }
604
605    private static function isRawEmptyValue(mixed $value): bool
606    {
607        return $value === null || $value === [] || $value === '';
608    }
609}

Paths

Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once. Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement always has an else as part of its logical flow even if you didn't write one.

ValidationRun->__construct
19        private readonly ShapeDefinition $shape,
20        private readonly array $data,
21    ) {
22        $this->errors = new ErrorBag();
23    }
ValidationRun->addReadErrors
267    private function addReadErrors(string $field, ReadValue $read, string|int|null $listIndex): void
268    {
269        if ($read->nestedResult !== null) {
 
270            $this->errors->addNested(self::path($field, $listIndex), $read->nestedResult);
271            $this->blockRules($field, $listIndex);
272        }
273
274        if ($read->issue !== null) {
 
274        if ($read->issue !== null) {
 
275            $this->errors->add(self::path($field, $listIndex), $read->issue);
276            $this->blockRules($field, $listIndex);
277        }
278    }
 
278    }
267    private function addReadErrors(string $field, ReadValue $read, string|int|null $listIndex): void
268    {
269        if ($read->nestedResult !== null) {
 
270            $this->errors->addNested(self::path($field, $listIndex), $read->nestedResult);
271            $this->blockRules($field, $listIndex);
272        }
273
274        if ($read->issue !== null) {
 
274        if ($read->issue !== null) {
 
278    }
267    private function addReadErrors(string $field, ReadValue $read, string|int|null $listIndex): void
268    {
269        if ($read->nestedResult !== null) {
 
274        if ($read->issue !== null) {
 
275            $this->errors->add(self::path($field, $listIndex), $read->issue);
276            $this->blockRules($field, $listIndex);
277        }
278    }
 
278    }
267    private function addReadErrors(string $field, ReadValue $read, string|int|null $listIndex): void
268    {
269        if ($read->nestedResult !== null) {
 
274        if ($read->issue !== null) {
 
278    }
ValidationRun->blockRules
576    private function blockRules(string $field, string|int|null $listIndex): void
577    {
578        $this->ruleBlockedPaths[self::pathKey(self::path($field, $listIndex))] = true;
579    }
ValidationRun->extractValues
166    private function extractValues(array $validatedValues): array
167    {
168        if ($this->shape->list) {
 
169            $values = [];
170
171            foreach ($validatedValues as $item) {
 
171            foreach ($validatedValues as $item) {
 
171            foreach ($validatedValues as $item) {
172                /** @var array<string, Value> $item */
173                $values[] = $this->getValues($item);
 
171            foreach ($validatedValues as $item) {
 
171            foreach ($validatedValues as $item) {
172                /** @var array<string, Value> $item */
173                $values[] = $this->getValues($item);
174            }
175
176            return $values;
166    private function extractValues(array $validatedValues): array
167    {
168        if ($this->shape->list) {
 
169            $values = [];
170
171            foreach ($validatedValues as $item) {
 
171            foreach ($validatedValues as $item) {
 
171            foreach ($validatedValues as $item) {
172                /** @var array<string, Value> $item */
173                $values[] = $this->getValues($item);
174            }
175
176            return $values;
166    private function extractValues(array $validatedValues): array
167    {
168        if ($this->shape->list) {
 
169            $values = [];
170
171            foreach ($validatedValues as $item) {
 
171            foreach ($validatedValues as $item) {
172                /** @var array<string, Value> $item */
173                $values[] = $this->getValues($item);
174            }
175
176            return $values;
166    private function extractValues(array $validatedValues): array
167    {
168        if ($this->shape->list) {
 
180        return $this->getValues($validatedValues);
181    }
ValidationRun->fillMissingFromFields
476        array $values,
477        array $data,
478        string|int|null $listIndex = null,
479    ): array {
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
482                continue;
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
500                self::path($field, $listIndex),
501                $this->formatMissingFailure($definition),
502            );
503        }
504
505        return $values;
506    }
476        array $values,
477        array $data,
478        string|int|null $listIndex = null,
479    ): array {
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
485            if ($definition->treatsMissingAsEmpty()) {
 
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
 
489                    $values[$field] = $value;
490                }
491
492                continue;
 
492                continue;
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
500                self::path($field, $listIndex),
501                $this->formatMissingFailure($definition),
502            );
503        }
504
505        return $values;
506    }
476        array $values,
477        array $data,
478        string|int|null $listIndex = null,
479    ): array {
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
485            if ($definition->treatsMissingAsEmpty()) {
 
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
 
492                continue;
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
500                self::path($field, $listIndex),
501                $this->formatMissingFailure($definition),
502            );
503        }
504
505        return $values;
506    }
476        array $values,
477        array $data,
478        string|int|null $listIndex = null,
479    ): array {
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
485            if ($definition->treatsMissingAsEmpty()) {
 
495            if ($definition->isOptional()) {
 
496                continue;
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
500                self::path($field, $listIndex),
501                $this->formatMissingFailure($definition),
502            );
503        }
504
505        return $values;
506    }
476        array $values,
477        array $data,
478        string|int|null $listIndex = null,
479    ): array {
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
485            if ($definition->treatsMissingAsEmpty()) {
 
495            if ($definition->isOptional()) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
500                self::path($field, $listIndex),
501                $this->formatMissingFailure($definition),
502            );
503        }
504
505        return $values;
506    }
476        array $values,
477        array $data,
478        string|int|null $listIndex = null,
479    ): array {
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
482                continue;
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
500                self::path($field, $listIndex),
501                $this->formatMissingFailure($definition),
502            );
503        }
504
505        return $values;
506    }
476        array $values,
477        array $data,
478        string|int|null $listIndex = null,
479    ): array {
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
485            if ($definition->treatsMissingAsEmpty()) {
 
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
 
489                    $values[$field] = $value;
490                }
491
492                continue;
 
492                continue;
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
500                self::path($field, $listIndex),
501                $this->formatMissingFailure($definition),
502            );
503        }
504
505        return $values;
506    }
476        array $values,
477        array $data,
478        string|int|null $listIndex = null,
479    ): array {
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
485            if ($definition->treatsMissingAsEmpty()) {
 
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
 
492                continue;
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
500                self::path($field, $listIndex),
501                $this->formatMissingFailure($definition),
502            );
503        }
504
505        return $values;
506    }
476        array $values,
477        array $data,
478        string|int|null $listIndex = null,
479    ): array {
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
485            if ($definition->treatsMissingAsEmpty()) {
 
495            if ($definition->isOptional()) {
 
496                continue;
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
500                self::path($field, $listIndex),
501                $this->formatMissingFailure($definition),
502            );
503        }
504
505        return $values;
506    }
476        array $values,
477        array $data,
478        string|int|null $listIndex = null,
479    ): array {
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
 
485            if ($definition->treatsMissingAsEmpty()) {
 
495            if ($definition->isOptional()) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
500                self::path($field, $listIndex),
501                $this->formatMissingFailure($definition),
502            );
503        }
504
505        return $values;
506    }
476        array $values,
477        array $data,
478        string|int|null $listIndex = null,
479    ): array {
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
500                self::path($field, $listIndex),
501                $this->formatMissingFailure($definition),
502            );
503        }
504
505        return $values;
506    }
476        array $values,
477        array $data,
478        string|int|null $listIndex = null,
479    ): array {
480        foreach ($this->shape->fields as $field => $definition) {
 
480        foreach ($this->shape->fields as $field => $definition) {
481            if (array_key_exists($field, $values) || array_key_exists($field, $data)) {
482                continue;
483            }
484
485            if ($definition->treatsMissingAsEmpty()) {
486                $value = $this->readEmptyValue($field, $definition, $data, $listIndex);
487
488                if ($value !== null) {
489                    $values[$field] = $value;
490                }
491
492                continue;
493            }
494
495            if ($definition->isOptional()) {
496                continue;
497            }
498
499            $this->errors->add(
500                self::path($field, $listIndex),
501                $this->formatMissingFailure($definition),
502            );
503        }
504
505        return $values;
506    }
ValidationRun->finalizeItem
142    private function finalizeItem(array $values): array
143    {
144        $itemValues = $this->getValues($values);
145
146        foreach ($this->shape->fields as $field => $definition) {
 
146        foreach ($this->shape->fields as $field => $definition) {
 
146        foreach ($this->shape->fields as $field => $definition) {
147            if (!array_key_exists($field, $values)) {
 
148                continue;
 
146        foreach ($this->shape->fields as $field => $definition) {
 
146        foreach ($this->shape->fields as $field => $definition) {
147            if (!array_key_exists($field, $values)) {
148                continue;
149            }
150
151            $value = $values[$field];
152            $values[$field] = new \Celema\Sire\Value(
153                $definition->applyFinalization($value->value, $itemValues),
154                $value->pristine,
155                $value->empty,
156            );
157        }
158
159        return $values;
160    }
142    private function finalizeItem(array $values): array
143    {
144        $itemValues = $this->getValues($values);
145
146        foreach ($this->shape->fields as $field => $definition) {
 
146        foreach ($this->shape->fields as $field => $definition) {
 
146        foreach ($this->shape->fields as $field => $definition) {
147            if (!array_key_exists($field, $values)) {
 
146        foreach ($this->shape->fields as $field => $definition) {
147            if (!array_key_exists($field, $values)) {
148                continue;
149            }
150
151            $value = $values[$field];
 
146        foreach ($this->shape->fields as $field => $definition) {
 
146        foreach ($this->shape->fields as $field => $definition) {
147            if (!array_key_exists($field, $values)) {
148                continue;
149            }
150
151            $value = $values[$field];
152            $values[$field] = new \Celema\Sire\Value(
153                $definition->applyFinalization($value->value, $itemValues),
154                $value->pristine,
155                $value->empty,
156            );
157        }
158
159        return $values;
160    }
142    private function finalizeItem(array $values): array
143    {
144        $itemValues = $this->getValues($values);
145
146        foreach ($this->shape->fields as $field => $definition) {
 
146        foreach ($this->shape->fields as $field => $definition) {
 
146        foreach ($this->shape->fields as $field => $definition) {
147            if (!array_key_exists($field, $values)) {
148                continue;
149            }
150
151            $value = $values[$field];
152            $values[$field] = new \Celema\Sire\Value(
153                $definition->applyFinalization($value->value, $itemValues),
154                $value->pristine,
155                $value->empty,
156            );
157        }
158
159        return $values;
160    }
142    private function finalizeItem(array $values): array
143    {
144        $itemValues = $this->getValues($values);
145
146        foreach ($this->shape->fields as $field => $definition) {
 
146        foreach ($this->shape->fields as $field => $definition) {
147            if (!array_key_exists($field, $values)) {
148                continue;
149            }
150
151            $value = $values[$field];
152            $values[$field] = new \Celema\Sire\Value(
153                $definition->applyFinalization($value->value, $itemValues),
154                $value->pristine,
155                $value->empty,
156            );
157        }
158
159        return $values;
160    }
ValidationRun->finalizeValues
121    private function finalizeValues(array $validatedValues): array
122    {
123        if ($this->shape->list) {
 
124            $values = [];
125
126            foreach ($validatedValues as $item) {
 
126            foreach ($validatedValues as $item) {
 
126            foreach ($validatedValues as $item) {
127                /** @var array<string, Value> $item */
128                $values[] = $this->finalizeItem($item);
 
126            foreach ($validatedValues as $item) {
 
126            foreach ($validatedValues as $item) {
127                /** @var array<string, Value> $item */
128                $values[] = $this->finalizeItem($item);
129            }
130
131            return $values;
121    private function finalizeValues(array $validatedValues): array
122    {
123        if ($this->shape->list) {
 
124            $values = [];
125
126            foreach ($validatedValues as $item) {
 
126            foreach ($validatedValues as $item) {
 
126            foreach ($validatedValues as $item) {
127                /** @var array<string, Value> $item */
128                $values[] = $this->finalizeItem($item);
129            }
130
131            return $values;
121    private function finalizeValues(array $validatedValues): array
122    {
123        if ($this->shape->list) {
 
124            $values = [];
125
126            foreach ($validatedValues as $item) {
 
126            foreach ($validatedValues as $item) {
127                /** @var array<string, Value> $item */
128                $values[] = $this->finalizeItem($item);
129            }
130
131            return $values;
121    private function finalizeValues(array $validatedValues): array
122    {
123        if ($this->shape->list) {
 
135        return $this->finalizeItem($validatedValues);
136    }
ValidationRun->formatCoercionFailure
365        Contract\Coercion $coercion,
366        Field $definition,
367        Contract\Coercer $coercer,
368    ): ?Issue {
369        if ($coercion->failure === null) {
 
370            return null;
365        Contract\Coercion $coercion,
366        Field $definition,
367        Contract\Coercer $coercer,
368    ): ?Issue {
369        if ($coercion->failure === null) {
 
373        return $this->formatFailure(
374            $coercion->failure,
375            $definition->name(),
376            $definition->field,
377            $coercion->pristine,
378            'type.' . $definition->type(),
379            $coercer->message,
380            messages: $definition->messageOverrides(),
381        );
382    }
ValidationRun->formatExtraFailure
326    private function formatExtraFailure(string $field, mixed $value): Issue
327    {
328        return $this->formatFailure(
329            Failure::invalid(),
330            $field,
331            $field,
332            $value,
333            'extra',
334            Messages::EXTRA,
335        );
336    }
ValidationRun->formatFailure
402        Failure $failure,
403        string $label,
404        string $field,
405        mixed $pristine,
406        ?string $defaultKey,
407        string $fallback,
408        array $args = [],
409        array $messages = [],
410    ): Issue {
411        $args = $failure->args === [] ? $args : $failure->args;
 
411        $args = $failure->args === [] ? $args : $failure->args;
 
411        $args = $failure->args === [] ? $args : $failure->args;
412
413        return new Issue(
414            [],
415            $failure->key !== '' ? $failure->key : $defaultKey ?? 'invalid',
 
415            $failure->key !== '' ? $failure->key : $defaultKey ?? 'invalid',
 
415            $failure->key !== '' ? $failure->key : $defaultKey ?? 'invalid',
416            $this->shape->messageFormatter->format(
417                $failure,
418                $label,
419                $field,
420                $pristine,
421                $defaultKey,
422                $fallback,
423                $args,
424                $messages,
425            ),
426            Arguments::keyed($args),
427        );
428    }
402        Failure $failure,
403        string $label,
404        string $field,
405        mixed $pristine,
406        ?string $defaultKey,
407        string $fallback,
408        array $args = [],
409        array $messages = [],
410    ): Issue {
411        $args = $failure->args === [] ? $args : $failure->args;
 
411        $args = $failure->args === [] ? $args : $failure->args;
 
411        $args = $failure->args === [] ? $args : $failure->args;
412
413        return new Issue(
414            [],
415            $failure->key !== '' ? $failure->key : $defaultKey ?? 'invalid',
 
415            $failure->key !== '' ? $failure->key : $defaultKey ?? 'invalid',
 
415            $failure->key !== '' ? $failure->key : $defaultKey ?? 'invalid',
416            $this->shape->messageFormatter->format(
417                $failure,
418                $label,
419                $field,
420                $pristine,
421                $defaultKey,
422                $fallback,
423                $args,
424                $messages,
425            ),
426            Arguments::keyed($args),
427        );
428    }
402        Failure $failure,
403        string $label,
404        string $field,
405        mixed $pristine,
406        ?string $defaultKey,
407        string $fallback,
408        array $args = [],
409        array $messages = [],
410    ): Issue {
411        $args = $failure->args === [] ? $args : $failure->args;
 
411        $args = $failure->args === [] ? $args : $failure->args;
 
411        $args = $failure->args === [] ? $args : $failure->args;
412
413        return new Issue(
414            [],
415            $failure->key !== '' ? $failure->key : $defaultKey ?? 'invalid',
 
415            $failure->key !== '' ? $failure->key : $defaultKey ?? 'invalid',
 
415            $failure->key !== '' ? $failure->key : $defaultKey ?? 'invalid',
416            $this->shape->messageFormatter->format(
417                $failure,
418                $label,
419                $field,
420                $pristine,
421                $defaultKey,
422                $fallback,
423                $args,
424                $messages,
425            ),
426            Arguments::keyed($args),
427        );
428    }
402        Failure $failure,
403        string $label,
404        string $field,
405        mixed $pristine,
406        ?string $defaultKey,
407        string $fallback,
408        array $args = [],
409        array $messages = [],
410    ): Issue {
411        $args = $failure->args === [] ? $args : $failure->args;
 
411        $args = $failure->args === [] ? $args : $failure->args;
 
411        $args = $failure->args === [] ? $args : $failure->args;
412
413        return new Issue(
414            [],
415            $failure->key !== '' ? $failure->key : $defaultKey ?? 'invalid',
 
415            $failure->key !== '' ? $failure->key : $defaultKey ?? 'invalid',
 
415            $failure->key !== '' ? $failure->key : $defaultKey ?? 'invalid',
416            $this->shape->messageFormatter->format(
417                $failure,
418                $label,
419                $field,
420                $pristine,
421                $defaultKey,
422                $fallback,
423                $args,
424                $messages,
425            ),
426            Arguments::keyed($args),
427        );
428    }
ValidationRun->formatMissingFailure
351    private function formatMissingFailure(Field $definition): Issue
352    {
353        return $this->formatFailure(
354            Failure::key('missing'),
355            $definition->name(),
356            $definition->field,
357            null,
358            'missing',
359            Messages::MISSING,
360            messages: $definition->messageOverrides(),
361        );
362    }
ValidationRun->formatNullFailure
338    private function formatNullFailure(Field $definition): Issue
339    {
340        return $this->formatFailure(
341            Failure::key('null'),
342            $definition->name(),
343            $definition->field,
344            null,
345            'null',
346            Messages::NULL,
347            messages: $definition->messageOverrides(),
348        );
349    }
ValidationRun->formatShapeFailure
384    private function formatShapeFailure(Field $definition, mixed $value): Issue
385    {
386        return $this->formatFailure(
387            Failure::invalid(),
388            $definition->name(),
389            $definition->field,
390            $value,
391            'type.shape',
392            Messages::SHAPE,
393            messages: $definition->messageOverrides(),
394        );
395    }
ValidationRun->getValues
568    private function getValues(array $values): array
569    {
570        return array_map(
571            static fn(Value $item): mixed => $item->value,
572            $values,
573        );
574    }
ValidationRun->isRawEmptyValue
605    private static function isRawEmptyValue(mixed $value): bool
606    {
607        return $value === null || $value === [] || $value === '';
 
607        return $value === null || $value === [] || $value === '';
 
607        return $value === null || $value === [] || $value === '';
 
607        return $value === null || $value === [] || $value === '';
 
607        return $value === null || $value === [] || $value === '';
608    }
605    private static function isRawEmptyValue(mixed $value): bool
606    {
607        return $value === null || $value === [] || $value === '';
 
607        return $value === null || $value === [] || $value === '';
 
607        return $value === null || $value === [] || $value === '';
 
607        return $value === null || $value === [] || $value === '';
608    }
605    private static function isRawEmptyValue(mixed $value): bool
606    {
607        return $value === null || $value === [] || $value === '';
 
607        return $value === null || $value === [] || $value === '';
 
607        return $value === null || $value === [] || $value === '';
 
607        return $value === null || $value === [] || $value === '';
608    }
605    private static function isRawEmptyValue(mixed $value): bool
606    {
607        return $value === null || $value === [] || $value === '';
 
607        return $value === null || $value === [] || $value === '';
 
607        return $value === null || $value === [] || $value === '';
608    }
ValidationRun->path
590    private static function path(string $field, string|int|null $listIndex): array
591    {
592        if ($listIndex === null) {
 
593            return [$field];
590    private static function path(string $field, string|int|null $listIndex): array
591    {
592        if ($listIndex === null) {
 
596        return [$listIndex, $field];
597    }
ValidationRun->pathKey
600    private static function pathKey(array $path): string
601    {
602        return serialize($path);
603    }
ValidationRun->prepareData
61    private function prepareData(array $data): array
62    {
63        foreach ($this->shape->prepareCallbacks as $prepare) {
 
63        foreach ($this->shape->prepareCallbacks as $prepare) {
 
63        foreach ($this->shape->prepareCallbacks as $prepare) {
64            $data = $prepare($data);
 
63        foreach ($this->shape->prepareCallbacks as $prepare) {
 
63        foreach ($this->shape->prepareCallbacks as $prepare) {
64            $data = $prepare($data);
65        }
66
67        return $data;
68    }
61    private function prepareData(array $data): array
62    {
63        foreach ($this->shape->prepareCallbacks as $prepare) {
 
63        foreach ($this->shape->prepareCallbacks as $prepare) {
 
63        foreach ($this->shape->prepareCallbacks as $prepare) {
64            $data = $prepare($data);
65        }
66
67        return $data;
68    }
61    private function prepareData(array $data): array
62    {
63        foreach ($this->shape->prepareCallbacks as $prepare) {
 
63        foreach ($this->shape->prepareCallbacks as $prepare) {
64            $data = $prepare($data);
65        }
66
67        return $data;
68    }
ValidationRun->readDefaultValue
253        string $field,
254        Field $definition,
255        array $data,
256        string|int|null $listIndex,
257    ): Value {
258        $read = $this->readKnownValue(
259            $definition,
260            $definition->applyPreparation($definition->defaultValue(), $data),
261        );
262        $this->addReadErrors($field, $read, $listIndex);
263
264        return $read->value;
265    }
ValidationRun->readEmptyValue
230        string $field,
231        Field $definition,
232        array $data,
233        string|int|null $listIndex,
234    ): ?Value {
235        if ($definition->hasDefault()) {
 
236            return $this->readDefaultValue($field, $definition, $data, $listIndex);
230        string $field,
231        Field $definition,
232        array $data,
233        string|int|null $listIndex,
234    ): ?Value {
235        if ($definition->hasDefault()) {
 
239        if ($definition->isOptional()) {
 
240            return null;
230        string $field,
231        Field $definition,
232        array $data,
233        string|int|null $listIndex,
234    ): ?Value {
235        if ($definition->hasDefault()) {
 
239        if ($definition->isOptional()) {
 
243        $this->errors->add(
244            self::path($field, $listIndex),
245            $this->formatMissingFailure($definition),
246        );
247
248        return null;
249    }
ValidationRun->readExtraValue
280    private function readExtraValue(string $field, mixed $value, string|int|null $listIndex): ?Value
281    {
282        if ($this->shape->extra === Extra::Allow) {
 
283            return new \Celema\Sire\Value($value, $value, self::isRawEmptyValue($value));
280    private function readExtraValue(string $field, mixed $value, string|int|null $listIndex): ?Value
281    {
282        if ($this->shape->extra === Extra::Allow) {
 
286        if ($this->shape->extra === Extra::Forbid) {
 
287            $this->errors->add(
288                self::path($field, $listIndex),
289                $this->formatExtraFailure($field, $value),
290            );
291        }
292
293        return null;
 
293        return null;
294    }
280    private function readExtraValue(string $field, mixed $value, string|int|null $listIndex): ?Value
281    {
282        if ($this->shape->extra === Extra::Allow) {
 
286        if ($this->shape->extra === Extra::Forbid) {
 
293        return null;
294    }
ValidationRun->readFieldValue
209        string $field,
210        Field $definition,
211        mixed $value,
212        array $data,
213        string|int|null $listIndex,
214    ): ?Value {
215        /** @var mixed $value */
216        $value = $definition->applyPreparation($value, $data);
217
218        if ($definition->isBlank($value)) {
 
219            return $this->readEmptyValue($field, $definition, $data, $listIndex);
209        string $field,
210        Field $definition,
211        mixed $value,
212        array $data,
213        string|int|null $listIndex,
214    ): ?Value {
215        /** @var mixed $value */
216        $value = $definition->applyPreparation($value, $data);
217
218        if ($definition->isBlank($value)) {
 
222        $read = $this->readKnownValue($definition, $value);
223        $this->addReadErrors($field, $read, $listIndex);
224
225        return $read->value;
226    }
ValidationRun->readFromData
187    private function readFromData(array $data, string|int|null $listIndex = null): array
188    {
189        $values = [];
190
191        /** @var mixed $value */
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
193            $field = (string) $field;
194            $definition = $this->shape->fields[$field] ?? null;
195            $value = $definition instanceof Field
 
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
 
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
198
199            if ($value !== null) {
 
192        foreach ($data as $field => $value) {
193            $field = (string) $field;
194            $definition = $this->shape->fields[$field] ?? null;
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
198
199            if ($value !== null) {
200                $values[$field] = $value;
 
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
193            $field = (string) $field;
194            $definition = $this->shape->fields[$field] ?? null;
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
198
199            if ($value !== null) {
200                $values[$field] = $value;
201            }
202        }
203
204        return $values;
205    }
187    private function readFromData(array $data, string|int|null $listIndex = null): array
188    {
189        $values = [];
190
191        /** @var mixed $value */
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
193            $field = (string) $field;
194            $definition = $this->shape->fields[$field] ?? null;
195            $value = $definition instanceof Field
 
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
 
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
198
199            if ($value !== null) {
 
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
193            $field = (string) $field;
194            $definition = $this->shape->fields[$field] ?? null;
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
198
199            if ($value !== null) {
200                $values[$field] = $value;
201            }
202        }
203
204        return $values;
205    }
187    private function readFromData(array $data, string|int|null $listIndex = null): array
188    {
189        $values = [];
190
191        /** @var mixed $value */
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
193            $field = (string) $field;
194            $definition = $this->shape->fields[$field] ?? null;
195            $value = $definition instanceof Field
 
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
 
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
198
199            if ($value !== null) {
 
192        foreach ($data as $field => $value) {
193            $field = (string) $field;
194            $definition = $this->shape->fields[$field] ?? null;
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
198
199            if ($value !== null) {
200                $values[$field] = $value;
 
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
193            $field = (string) $field;
194            $definition = $this->shape->fields[$field] ?? null;
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
198
199            if ($value !== null) {
200                $values[$field] = $value;
201            }
202        }
203
204        return $values;
205    }
187    private function readFromData(array $data, string|int|null $listIndex = null): array
188    {
189        $values = [];
190
191        /** @var mixed $value */
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
193            $field = (string) $field;
194            $definition = $this->shape->fields[$field] ?? null;
195            $value = $definition instanceof Field
 
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
 
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
198
199            if ($value !== null) {
 
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
193            $field = (string) $field;
194            $definition = $this->shape->fields[$field] ?? null;
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
198
199            if ($value !== null) {
200                $values[$field] = $value;
201            }
202        }
203
204        return $values;
205    }
187    private function readFromData(array $data, string|int|null $listIndex = null): array
188    {
189        $values = [];
190
191        /** @var mixed $value */
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
193            $field = (string) $field;
194            $definition = $this->shape->fields[$field] ?? null;
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
198
199            if ($value !== null) {
200                $values[$field] = $value;
201            }
202        }
203
204        return $values;
205    }
187    private function readFromData(array $data, string|int|null $listIndex = null): array
188    {
189        $values = [];
190
191        /** @var mixed $value */
192        foreach ($data as $field => $value) {
 
192        foreach ($data as $field => $value) {
193            $field = (string) $field;
194            $definition = $this->shape->fields[$field] ?? null;
195            $value = $definition instanceof Field
196                ? $this->readFieldValue($field, $definition, $value, $data, $listIndex)
197                : $this->readExtraValue($field, $value, $listIndex);
198
199            if ($value !== null) {
200                $values[$field] = $value;
201            }
202        }
203
204        return $values;
205    }
ValidationRun->readKnownValue
296    private function readKnownValue(Field $definition, mixed $value): ReadValue
297    {
298        if ($value === null) {
 
299            return new ReadValue(
300                new \Celema\Sire\Value(null, null, true),
301                $definition->isNullable() ? null : $this->formatNullFailure($definition),
 
301                $definition->isNullable() ? null : $this->formatNullFailure($definition),
 
301                $definition->isNullable() ? null : $this->formatNullFailure($definition),
296    private function readKnownValue(Field $definition, mixed $value): ReadValue
297    {
298        if ($value === null) {
 
299            return new ReadValue(
300                new \Celema\Sire\Value(null, null, true),
301                $definition->isNullable() ? null : $this->formatNullFailure($definition),
 
301                $definition->isNullable() ? null : $this->formatNullFailure($definition),
 
301                $definition->isNullable() ? null : $this->formatNullFailure($definition),
296    private function readKnownValue(Field $definition, mixed $value): ReadValue
297    {
298        if ($value === null) {
 
305        if ($definition->type instanceof Contract\Validator) {
 
306            return $this->toSubValues($value, $definition, $definition->type);
296    private function readKnownValue(Field $definition, mixed $value): ReadValue
297    {
298        if ($value === null) {
 
305        if ($definition->type instanceof Contract\Validator) {
 
309        $coercer = $this->shape->coercers->get($definition->type);
310
311        if ($coercer === null) {
 
312            throw new ValueError('Wrong shape type');
296    private function readKnownValue(Field $definition, mixed $value): ReadValue
297    {
298        if ($value === null) {
 
305        if ($definition->type instanceof Contract\Validator) {
 
309        $coercer = $this->shape->coercers->get($definition->type);
310
311        if ($coercer === null) {
 
315        $coercion = $coercer->coerce(
316            $value,
317            $definition->coercionMode($this->shape->coercionMode),
318        );
319
320        return new ReadValue(
321            new \Celema\Sire\Value($coercion->value, $coercion->pristine, $coercion->empty),
322            $this->formatCoercionFailure($coercion, $definition, $coercer),
323        );
324    }
ValidationRun->readValues
512    private function readValues(array $data): array
513    {
514        if ($this->shape->list) {
 
515            $values = [];
516
517            foreach ($data as $listIndex => $item) {
 
517            foreach ($data as $listIndex => $item) {
 
517            foreach ($data as $listIndex => $item) {
518                if (!is_array($item)) {
 
519                    $this->errors->add(
520                        [$listIndex],
521                        new Issue([], 'type.shape', 'Item must be an array'),
522                    );
523                    $values[] = [];
524
525                    continue;
 
517            foreach ($data as $listIndex => $item) {
 
517            foreach ($data as $listIndex => $item) {
518                if (!is_array($item)) {
519                    $this->errors->add(
520                        [$listIndex],
521                        new Issue([], 'type.shape', 'Item must be an array'),
522                    );
523                    $values[] = [];
524
525                    continue;
526                }
527
528                $subValues = $this->readFromData($item, $listIndex);
529                $values[] = $this->fillMissingFromFields($subValues, $item, $listIndex);
530            }
531
532            return $values;
512    private function readValues(array $data): array
513    {
514        if ($this->shape->list) {
 
515            $values = [];
516
517            foreach ($data as $listIndex => $item) {
 
517            foreach ($data as $listIndex => $item) {
 
517            foreach ($data as $listIndex => $item) {
518                if (!is_array($item)) {
 
517            foreach ($data as $listIndex => $item) {
518                if (!is_array($item)) {
519                    $this->errors->add(
520                        [$listIndex],
521                        new Issue([], 'type.shape', 'Item must be an array'),
522                    );
523                    $values[] = [];
524
525                    continue;
526                }
527
528                $subValues = $this->readFromData($item, $listIndex);
 
517            foreach ($data as $listIndex => $item) {
 
517            foreach ($data as $listIndex => $item) {
518                if (!is_array($item)) {
519                    $this->errors->add(
520                        [$listIndex],
521                        new Issue([], 'type.shape', 'Item must be an array'),
522                    );
523                    $values[] = [];
524
525                    continue;
526                }
527
528                $subValues = $this->readFromData($item, $listIndex);
529                $values[] = $this->fillMissingFromFields($subValues, $item, $listIndex);
530            }
531
532            return $values;
512    private function readValues(array $data): array
513    {
514        if ($this->shape->list) {
 
515            $values = [];
516
517            foreach ($data as $listIndex => $item) {
 
517            foreach ($data as $listIndex => $item) {
 
517            foreach ($data as $listIndex => $item) {
518                if (!is_array($item)) {
519                    $this->errors->add(
520                        [$listIndex],
521                        new Issue([], 'type.shape', 'Item must be an array'),
522                    );
523                    $values[] = [];
524
525                    continue;
526                }
527
528                $subValues = $this->readFromData($item, $listIndex);
529                $values[] = $this->fillMissingFromFields($subValues, $item, $listIndex);
530            }
531
532            return $values;
512    private function readValues(array $data): array
513    {
514        if ($this->shape->list) {
 
515            $values = [];
516
517            foreach ($data as $listIndex => $item) {
 
517            foreach ($data as $listIndex => $item) {
518                if (!is_array($item)) {
519                    $this->errors->add(
520                        [$listIndex],
521                        new Issue([], 'type.shape', 'Item must be an array'),
522                    );
523                    $values[] = [];
524
525                    continue;
526                }
527
528                $subValues = $this->readFromData($item, $listIndex);
529                $values[] = $this->fillMissingFromFields($subValues, $item, $listIndex);
530            }
531
532            return $values;
512    private function readValues(array $data): array
513    {
514        if ($this->shape->list) {
 
535        $values = $this->readFromData($data);
536
537        return $this->fillMissingFromFields($values, $data);
538    }
ValidationRun->review
457    private function review(array $values): void
458    {
459        $context = new Review(
460            $this->errors,
461            $values,
462            $this->shape->list,
463        );
464
465        foreach ($this->shape->reviewCallbacks as $review) {
 
465        foreach ($this->shape->reviewCallbacks as $review) {
 
465        foreach ($this->shape->reviewCallbacks as $review) {
466            $review($context);
 
465        foreach ($this->shape->reviewCallbacks as $review) {
 
465        foreach ($this->shape->reviewCallbacks as $review) {
466            $review($context);
467        }
468    }
457    private function review(array $values): void
458    {
459        $context = new Review(
460            $this->errors,
461            $values,
462            $this->shape->list,
463        );
464
465        foreach ($this->shape->reviewCallbacks as $review) {
 
465        foreach ($this->shape->reviewCallbacks as $review) {
 
465        foreach ($this->shape->reviewCallbacks as $review) {
466            $review($context);
467        }
468    }
457    private function review(array $values): void
458    {
459        $context = new Review(
460            $this->errors,
461            $values,
462            $this->shape->list,
463        );
464
465        foreach ($this->shape->reviewCallbacks as $review) {
 
465        foreach ($this->shape->reviewCallbacks as $review) {
466            $review($context);
467        }
468    }
ValidationRun->rulesBlocked
581    private function rulesBlocked(string $field, string|int|null $listIndex): bool
582    {
583        return array_key_exists(
584            self::pathKey(self::path($field, $listIndex)),
585            $this->ruleBlockedPaths,
586        );
587    }
ValidationRun->toSubValues
431        mixed $pristine,
432        Field $definition,
433        Contract\Validator $shape,
434    ): ReadValue {
435        if (!is_array($pristine)) {
 
436            return new ReadValue(
437                new \Celema\Sire\Value($pristine, $pristine, self::isRawEmptyValue($pristine)),
438                $this->formatShapeFailure($definition, $pristine),
431        mixed $pristine,
432        Field $definition,
433        Contract\Validator $shape,
434    ): ReadValue {
435        if (!is_array($pristine)) {
 
442        $result = $shape->validate($pristine);
443
444        if ($result->valid()) {
 
445            $values = $result->values();
446
447            return new ReadValue(new \Celema\Sire\Value($values, $pristine, $values === []));
431        mixed $pristine,
432        Field $definition,
433        Contract\Validator $shape,
434    ): ReadValue {
435        if (!is_array($pristine)) {
 
442        $result = $shape->validate($pristine);
443
444        if ($result->valid()) {
 
450        return new ReadValue(
451            new \Celema\Sire\Value($pristine, $pristine, self::isRawEmptyValue($pristine)),
452            nestedResult: $result,
453        );
454    }
ValidationRun->validate
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
31        if ($this->shape->list) {
32            foreach ($values as $listIndex => $subValues) {
 
41        if (!$this->errors->hasErrors()) {
 
42            $validatedValues = $this->finalizeValues($validatedValues);
43        }
44
45        $extractedValues = $this->extractValues($validatedValues);
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
48            $this->review($extractedValues);
49        }
50
51        return new Result(
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
31        if ($this->shape->list) {
32            foreach ($values as $listIndex => $subValues) {
 
41        if (!$this->errors->hasErrors()) {
 
42            $validatedValues = $this->finalizeValues($validatedValues);
43        }
44
45        $extractedValues = $this->extractValues($validatedValues);
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
31        if ($this->shape->list) {
32            foreach ($values as $listIndex => $subValues) {
 
41        if (!$this->errors->hasErrors()) {
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
48            $this->review($extractedValues);
49        }
50
51        return new Result(
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
31        if ($this->shape->list) {
32            foreach ($values as $listIndex => $subValues) {
 
41        if (!$this->errors->hasErrors()) {
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
31        if ($this->shape->list) {
32            foreach ($values as $listIndex => $subValues) {
 
41        if (!$this->errors->hasErrors()) {
 
42            $validatedValues = $this->finalizeValues($validatedValues);
43        }
44
45        $extractedValues = $this->extractValues($validatedValues);
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
48            $this->review($extractedValues);
49        }
50
51        return new Result(
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
31        if ($this->shape->list) {
32            foreach ($values as $listIndex => $subValues) {
 
41        if (!$this->errors->hasErrors()) {
 
42            $validatedValues = $this->finalizeValues($validatedValues);
43        }
44
45        $extractedValues = $this->extractValues($validatedValues);
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
31        if ($this->shape->list) {
32            foreach ($values as $listIndex => $subValues) {
 
41        if (!$this->errors->hasErrors()) {
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
48            $this->review($extractedValues);
49        }
50
51        return new Result(
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
32            foreach ($values as $listIndex => $subValues) {
 
32            foreach ($values as $listIndex => $subValues) {
 
31        if ($this->shape->list) {
32            foreach ($values as $listIndex => $subValues) {
 
41        if (!$this->errors->hasErrors()) {
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
32            foreach ($values as $listIndex => $subValues) {
 
31        if ($this->shape->list) {
32            foreach ($values as $listIndex => $subValues) {
 
41        if (!$this->errors->hasErrors()) {
 
42            $validatedValues = $this->finalizeValues($validatedValues);
43        }
44
45        $extractedValues = $this->extractValues($validatedValues);
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
48            $this->review($extractedValues);
49        }
50
51        return new Result(
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
32            foreach ($values as $listIndex => $subValues) {
 
31        if ($this->shape->list) {
32            foreach ($values as $listIndex => $subValues) {
 
41        if (!$this->errors->hasErrors()) {
 
42            $validatedValues = $this->finalizeValues($validatedValues);
43        }
44
45        $extractedValues = $this->extractValues($validatedValues);
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
32            foreach ($values as $listIndex => $subValues) {
 
31        if ($this->shape->list) {
32            foreach ($values as $listIndex => $subValues) {
 
41        if (!$this->errors->hasErrors()) {
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
48            $this->review($extractedValues);
49        }
50
51        return new Result(
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
32            foreach ($values as $listIndex => $subValues) {
 
31        if ($this->shape->list) {
32            foreach ($values as $listIndex => $subValues) {
 
41        if (!$this->errors->hasErrors()) {
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
38            $validatedValues = $this->validateItem($values);
39        }
40
41        if (!$this->errors->hasErrors()) {
 
41        if (!$this->errors->hasErrors()) {
 
42            $validatedValues = $this->finalizeValues($validatedValues);
43        }
44
45        $extractedValues = $this->extractValues($validatedValues);
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
48            $this->review($extractedValues);
49        }
50
51        return new Result(
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
38            $validatedValues = $this->validateItem($values);
39        }
40
41        if (!$this->errors->hasErrors()) {
 
41        if (!$this->errors->hasErrors()) {
 
42            $validatedValues = $this->finalizeValues($validatedValues);
43        }
44
45        $extractedValues = $this->extractValues($validatedValues);
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
38            $validatedValues = $this->validateItem($values);
39        }
40
41        if (!$this->errors->hasErrors()) {
 
41        if (!$this->errors->hasErrors()) {
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
48            $this->review($extractedValues);
49        }
50
51        return new Result(
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
27        $data = $this->prepareData($this->data);
28        $values = $this->readValues($data);
29        $validatedValues = [];
30
31        if ($this->shape->list) {
 
38            $validatedValues = $this->validateItem($values);
39        }
40
41        if (!$this->errors->hasErrors()) {
 
41        if (!$this->errors->hasErrors()) {
 
45        $extractedValues = $this->extractValues($validatedValues);
46
47        if (!$this->errors->hasErrors()) {
 
51        return new Result(
52            $this->errors->issues(),
53            $extractedValues,
54        );
55    }
ValidationRun->validateField
71        Field $definition,
72        Value $value,
73        string $ruleDefinition,
74        string|int|null $listIndex,
75    ): void {
76        $parsedRule = $this->shape->ruleParser->parse($ruleDefinition);
77        $ruleName = $parsedRule['name'];
78        $ruleArgs = $parsedRule['args'];
79
80        $rule = $this->shape->rules->get($ruleName);
81
82        if ($rule === null) {
 
83            throw new ValueError(
84                sprintf('Unknown rule "%s" in field "%s"', $ruleName, $definition->field),
71        Field $definition,
72        Value $value,
73        string $ruleDefinition,
74        string|int|null $listIndex,
75    ): void {
76        $parsedRule = $this->shape->ruleParser->parse($ruleDefinition);
77        $ruleName = $parsedRule['name'];
78        $ruleArgs = $parsedRule['args'];
79
80        $rule = $this->shape->rules->get($ruleName);
81
82        if ($rule === null) {
 
88        if ($this->rulesBlocked($definition->field, $listIndex)) {
 
89            return;
71        Field $definition,
72        Value $value,
73        string $ruleDefinition,
74        string|int|null $listIndex,
75    ): void {
76        $parsedRule = $this->shape->ruleParser->parse($ruleDefinition);
77        $ruleName = $parsedRule['name'];
78        $ruleArgs = $parsedRule['args'];
79
80        $rule = $this->shape->rules->get($ruleName);
81
82        if ($rule === null) {
 
88        if ($this->rulesBlocked($definition->field, $listIndex)) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
93            return;
71        Field $definition,
72        Value $value,
73        string $ruleDefinition,
74        string|int|null $listIndex,
75    ): void {
76        $parsedRule = $this->shape->ruleParser->parse($ruleDefinition);
77        $ruleName = $parsedRule['name'];
78        $ruleArgs = $parsedRule['args'];
79
80        $rule = $this->shape->rules->get($ruleName);
81
82        if ($rule === null) {
 
88        if ($this->rulesBlocked($definition->field, $listIndex)) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
96        $validation = $rule->validate($value, ...$ruleArgs);
97
98        if ($validation->failure !== null) {
 
99            $this->errors->add(
100                self::path($definition->field, $listIndex),
101                $this->formatFailure(
102                    $validation->failure,
103                    $definition->name(),
104                    $definition->field,
105                    $value->pristine,
106                    'rule.' . $ruleName,
107                    $rule->message,
108                    $rule instanceof Contract\NamedArguments
 
109                        ? Arguments::named($ruleArgs, $rule)
 
110                        : $ruleArgs,
111                    $definition->messageOverrides(),
112                ),
113            );
114        }
115    }
 
115    }
71        Field $definition,
72        Value $value,
73        string $ruleDefinition,
74        string|int|null $listIndex,
75    ): void {
76        $parsedRule = $this->shape->ruleParser->parse($ruleDefinition);
77        $ruleName = $parsedRule['name'];
78        $ruleArgs = $parsedRule['args'];
79
80        $rule = $this->shape->rules->get($ruleName);
81
82        if ($rule === null) {
 
88        if ($this->rulesBlocked($definition->field, $listIndex)) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
96        $validation = $rule->validate($value, ...$ruleArgs);
97
98        if ($validation->failure !== null) {
 
99            $this->errors->add(
100                self::path($definition->field, $listIndex),
101                $this->formatFailure(
102                    $validation->failure,
103                    $definition->name(),
104                    $definition->field,
105                    $value->pristine,
106                    'rule.' . $ruleName,
107                    $rule->message,
108                    $rule instanceof Contract\NamedArguments
 
110                        : $ruleArgs,
 
110                        : $ruleArgs,
111                    $definition->messageOverrides(),
112                ),
113            );
114        }
115    }
 
115    }
71        Field $definition,
72        Value $value,
73        string $ruleDefinition,
74        string|int|null $listIndex,
75    ): void {
76        $parsedRule = $this->shape->ruleParser->parse($ruleDefinition);
77        $ruleName = $parsedRule['name'];
78        $ruleArgs = $parsedRule['args'];
79
80        $rule = $this->shape->rules->get($ruleName);
81
82        if ($rule === null) {
 
88        if ($this->rulesBlocked($definition->field, $listIndex)) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
96        $validation = $rule->validate($value, ...$ruleArgs);
97
98        if ($validation->failure !== null) {
 
115    }
71        Field $definition,
72        Value $value,
73        string $ruleDefinition,
74        string|int|null $listIndex,
75    ): void {
76        $parsedRule = $this->shape->ruleParser->parse($ruleDefinition);
77        $ruleName = $parsedRule['name'];
78        $ruleArgs = $parsedRule['args'];
79
80        $rule = $this->shape->rules->get($ruleName);
81
82        if ($rule === null) {
 
88        if ($this->rulesBlocked($definition->field, $listIndex)) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
93            return;
71        Field $definition,
72        Value $value,
73        string $ruleDefinition,
74        string|int|null $listIndex,
75    ): void {
76        $parsedRule = $this->shape->ruleParser->parse($ruleDefinition);
77        $ruleName = $parsedRule['name'];
78        $ruleArgs = $parsedRule['args'];
79
80        $rule = $this->shape->rules->get($ruleName);
81
82        if ($rule === null) {
 
88        if ($this->rulesBlocked($definition->field, $listIndex)) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
96        $validation = $rule->validate($value, ...$ruleArgs);
97
98        if ($validation->failure !== null) {
 
99            $this->errors->add(
100                self::path($definition->field, $listIndex),
101                $this->formatFailure(
102                    $validation->failure,
103                    $definition->name(),
104                    $definition->field,
105                    $value->pristine,
106                    'rule.' . $ruleName,
107                    $rule->message,
108                    $rule instanceof Contract\NamedArguments
 
109                        ? Arguments::named($ruleArgs, $rule)
 
110                        : $ruleArgs,
111                    $definition->messageOverrides(),
112                ),
113            );
114        }
115    }
 
115    }
71        Field $definition,
72        Value $value,
73        string $ruleDefinition,
74        string|int|null $listIndex,
75    ): void {
76        $parsedRule = $this->shape->ruleParser->parse($ruleDefinition);
77        $ruleName = $parsedRule['name'];
78        $ruleArgs = $parsedRule['args'];
79
80        $rule = $this->shape->rules->get($ruleName);
81
82        if ($rule === null) {
 
88        if ($this->rulesBlocked($definition->field, $listIndex)) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
96        $validation = $rule->validate($value, ...$ruleArgs);
97
98        if ($validation->failure !== null) {
 
99            $this->errors->add(
100                self::path($definition->field, $listIndex),
101                $this->formatFailure(
102                    $validation->failure,
103                    $definition->name(),
104                    $definition->field,
105                    $value->pristine,
106                    'rule.' . $ruleName,
107                    $rule->message,
108                    $rule instanceof Contract\NamedArguments
 
110                        : $ruleArgs,
 
110                        : $ruleArgs,
111                    $definition->messageOverrides(),
112                ),
113            );
114        }
115    }
 
115    }
71        Field $definition,
72        Value $value,
73        string $ruleDefinition,
74        string|int|null $listIndex,
75    ): void {
76        $parsedRule = $this->shape->ruleParser->parse($ruleDefinition);
77        $ruleName = $parsedRule['name'];
78        $ruleArgs = $parsedRule['args'];
79
80        $rule = $this->shape->rules->get($ruleName);
81
82        if ($rule === null) {
 
88        if ($this->rulesBlocked($definition->field, $listIndex)) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
92        if (!$rule instanceof Contract\ValidatesEmpty && $value->empty) {
 
96        $validation = $rule->validate($value, ...$ruleArgs);
97
98        if ($validation->failure !== null) {
 
115    }
ValidationRun->validateItem
544    private function validateItem(array $values, string|int|null $listIndex = null): array
545    {
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
 
548                continue;
 
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
548                continue;
549            }
550
551            foreach ($definition->rules as $rule) {
552                $this->validateField(
553                    $definition,
554                    $values[$field],
555                    $rule,
556                    $listIndex,
557                );
558            }
559        }
560
561        return $values;
562    }
544    private function validateItem(array $values, string|int|null $listIndex = null): array
545    {
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
 
551            foreach ($definition->rules as $rule) {
 
551            foreach ($definition->rules as $rule) {
 
551            foreach ($definition->rules as $rule) {
552                $this->validateField(
 
551            foreach ($definition->rules as $rule) {
 
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
548                continue;
549            }
550
551            foreach ($definition->rules as $rule) {
 
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
548                continue;
549            }
550
551            foreach ($definition->rules as $rule) {
552                $this->validateField(
553                    $definition,
554                    $values[$field],
555                    $rule,
556                    $listIndex,
557                );
558            }
559        }
560
561        return $values;
562    }
544    private function validateItem(array $values, string|int|null $listIndex = null): array
545    {
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
 
551            foreach ($definition->rules as $rule) {
 
551            foreach ($definition->rules as $rule) {
 
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
548                continue;
549            }
550
551            foreach ($definition->rules as $rule) {
 
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
548                continue;
549            }
550
551            foreach ($definition->rules as $rule) {
552                $this->validateField(
553                    $definition,
554                    $values[$field],
555                    $rule,
556                    $listIndex,
557                );
558            }
559        }
560
561        return $values;
562    }
544    private function validateItem(array $values, string|int|null $listIndex = null): array
545    {
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
 
551            foreach ($definition->rules as $rule) {
 
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
548                continue;
549            }
550
551            foreach ($definition->rules as $rule) {
 
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
548                continue;
549            }
550
551            foreach ($definition->rules as $rule) {
552                $this->validateField(
553                    $definition,
554                    $values[$field],
555                    $rule,
556                    $listIndex,
557                );
558            }
559        }
560
561        return $values;
562    }
544    private function validateItem(array $values, string|int|null $listIndex = null): array
545    {
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
548                continue;
549            }
550
551            foreach ($definition->rules as $rule) {
552                $this->validateField(
553                    $definition,
554                    $values[$field],
555                    $rule,
556                    $listIndex,
557                );
558            }
559        }
560
561        return $values;
562    }
544    private function validateItem(array $values, string|int|null $listIndex = null): array
545    {
546        foreach ($this->shape->fields as $field => $definition) {
 
546        foreach ($this->shape->fields as $field => $definition) {
547            if (!array_key_exists($field, $values)) {
548                continue;
549            }
550
551            foreach ($definition->rules as $rule) {
552                $this->validateField(
553                    $definition,
554                    $values[$field],
555                    $rule,
556                    $listIndex,
557                );
558            }
559        }
560
561        return $values;
562    }
{closure:/workspace/celema/sire/src/ValidationRun.php:571-571}
571            static fn(Value $item): mixed => $item->value,