Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
49 / 49
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
20 / 20
CRAP
100.00% covered (success)
100.00%
1 / 1
Shape
100.00% covered (success)
100.00%
49 / 49
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
20 / 20
22
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
 defaultMessages
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
 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 Celema\Sire;
6
7use Celema\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    /**
98     * Every message key the shape's rules and types can produce, with its
99     * default template before any override, sorted by key.
100     *
101     * @return array<string, string>
102     */
103    public function defaultMessages(): array
104    {
105        return $this->config->defaultMessages();
106    }
107
108    public function types(Contract\CoercerRegistry $registry): self
109    {
110        $this->config->coercers($registry);
111
112        return $this;
113    }
114
115    public function ruleParser(Contract\RuleParser $parser): self
116    {
117        $this->config->ruleParser($parser);
118
119        return $this;
120    }
121
122    /** @param Closure(array<array-key, mixed>): array<array-key, mixed> $callback */
123    public function prepare(Closure $callback): self
124    {
125        $this->prepareCallbacks[] = $callback;
126
127        return $this;
128    }
129
130    public function add(
131        string $field,
132        string|Contract\Validator $type,
133    ): Field {
134        if ($field === '') {
135            throw new ValueError(
136                'Shape definition error: field must not be empty',
137            );
138        }
139
140        $definition = new Field($field, $type);
141        $this->fields[$field] = $definition;
142
143        return $definition;
144    }
145
146    /** @param Closure(Review): void $callback */
147    public function review(Closure $callback): self
148    {
149        $this->reviewCallbacks[] = $callback;
150
151        return $this;
152    }
153
154    #[Override]
155    public function validate(array $data): Result
156    {
157        return new ValidationRun(
158            $this->definition(),
159            $data,
160        )->validate();
161    }
162
163    /**
164     * @return array<array-key, mixed>
165     * @throws ValidationError
166     */
167    #[Override]
168    public function parse(array $data): array
169    {
170        $result = $this->validate($data);
171
172        if (!$result->valid()) {
173            throw new ValidationError($result);
174        }
175
176        return $result->values();
177    }
178
179    private function definition(): ShapeDefinition
180    {
181        return $this->config->definition(
182            $this->fields,
183            $this->prepareCallbacks,
184            $this->reviewCallbacks,
185        );
186    }
187}