Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
19 / 19
CRAP
100.00% covered (success)
100.00%
1 / 1
Shape
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
19 / 19
21
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
 list
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
 asList
100.00% covered (success)
100.00%
2 / 2
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
 extra
100.00% covered (success)
100.00%
2 / 2
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
 strict
100.00% covered (success)
100.00%
2 / 2
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
 coerce
100.00% covered (success)
100.00%
2 / 2
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
 rule
100.00% covered (success)
100.00%
2 / 2
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
 rules
100.00% covered (success)
100.00%
2 / 2
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
 type
100.00% covered (success)
100.00%
2 / 2
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
 message
100.00% covered (success)
100.00%
2 / 2
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
 messages
100.00% covered (success)
100.00%
2 / 2
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
 types
100.00% covered (success)
100.00%
2 / 2
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
 ruleParser
100.00% covered (success)
100.00%
2 / 2
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
 prepare
100.00% covered (success)
100.00%
2 / 2
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
 add
100.00% covered (success)
100.00%
7 / 7
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
 review
100.00% covered (success)
100.00%
2 / 2
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%
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
 parse
100.00% covered (success)
100.00%
4 / 4
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
 definition
100.00% covered (success)
100.00%
5 / 5
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
1<?php
2
3declare(strict_types=1);
4
5namespace Celemas\Sire;
6
7use Celemas\Sire\Exception\ValidationError;
8use Closure;
9use Override;
10use ValueError;
11
12/** @api */
13final class Shape implements Contract\Shape
14{
15    private Config $config;
16    /** @var array<string, Field> */
17    private array $fields = [];
18    /** @var list<Closure(array<array-key, mixed>): array<array-key, mixed>> */
19    private array $prepareCallbacks = [];
20    /** @var list<Closure(Review): void> */
21    private array $reviewCallbacks = [];
22
23    public function __construct()
24    {
25        $this->config = new Config();
26    }
27
28    public static function list(): self
29    {
30        return new self()->asList();
31    }
32
33    public function asList(bool $list = true): self
34    {
35        $this->config->asList($list);
36
37        return $this;
38    }
39
40    public function extra(Extra|string $extra): self
41    {
42        $this->config->extra($extra);
43
44        return $this;
45    }
46
47    public function strict(): self
48    {
49        $this->config->coercionMode(CoercionMode::Strict);
50
51        return $this;
52    }
53
54    public function coerce(): self
55    {
56        $this->config->coercionMode(CoercionMode::Coerce);
57
58        return $this;
59    }
60
61    public function rule(string $name, Contract\Rule $rule): self
62    {
63        $this->config->rule($name, $rule);
64
65        return $this;
66    }
67
68    public function rules(Contract\RuleRegistry $registry): self
69    {
70        $this->config->rules($registry);
71
72        return $this;
73    }
74
75    public function type(string $name, Contract\Coercer $coercer): self
76    {
77        $this->config->coercer($name, $coercer);
78
79        return $this;
80    }
81
82    public function message(string $key, string $message): self
83    {
84        $this->config->message($key, $message);
85
86        return $this;
87    }
88
89    /** @param array<string, string> $messages */
90    public function messages(array $messages): self
91    {
92        $this->config->messages($messages);
93
94        return $this;
95    }
96
97    public function types(Contract\CoercerRegistry $registry): self
98    {
99        $this->config->coercers($registry);
100
101        return $this;
102    }
103
104    public function ruleParser(Contract\RuleParser $parser): self
105    {
106        $this->config->ruleParser($parser);
107
108        return $this;
109    }
110
111    /** @param Closure(array<array-key, mixed>): array<array-key, mixed> $callback */
112    public function prepare(Closure $callback): self
113    {
114        $this->prepareCallbacks[] = $callback;
115
116        return $this;
117    }
118
119    public function add(
120        string $field,
121        string|Contract\Validator $type,
122    ): Field {
123        if (!$field) {
124            throw new ValueError(
125                'Shape definition error: field must not be empty',
126            );
127        }
128
129        $definition = new Field($field, $type);
130        $this->fields[$field] = $definition;
131
132        return $definition;
133    }
134
135    /** @param Closure(Review): void $callback */
136    public function review(Closure $callback): self
137    {
138        $this->reviewCallbacks[] = $callback;
139
140        return $this;
141    }
142
143    #[Override]
144    public function validate(array $data): Result
145    {
146        return new ValidationRun(
147            $this->definition(),
148            $data,
149        )->validate();
150    }
151
152    /**
153     * @return array<array-key, mixed>
154     * @throws ValidationError
155     */
156    #[Override]
157    public function parse(array $data): array
158    {
159        $result = $this->validate($data);
160
161        if (!$result->valid()) {
162            throw new ValidationError($result);
163        }
164
165        return $result->values();
166    }
167
168    private function definition(): ShapeDefinition
169    {
170        return $this->config->definition(
171            $this->fields,
172            $this->prepareCallbacks,
173            $this->reviewCallbacks,
174        );
175    }
176}