Lines 100.00% 13 / 13
Methods 100.00% 5 / 5
Classes 100.00% 1 / 1
Name Lines Methods CRAP
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 claim 100.00% 7 / 7 100.00% 1 / 1 3
 claimExactly 100.00% 3 / 3 100.00% 1 / 1 3
 isReserved 100.00% 1 / 1 100.00% 1 / 1 1
 isTaken 100.00% 1 / 1 100.00% 1 / 1 1
9final class IdentifierAllocator
10{
11    /** @var array<string, true> */
12    private array $taken = [];
13
14    /** @param list<string> $reserved */
15    public function __construct(
16        private readonly array $reserved,
17        private readonly string $suffix,
18    ) {}
19
20    public function claim(string $preferred): string
21    {
22        $base = $this->isReserved($preferred) ? $preferred . $this->suffix : $preferred;
23        $candidate = $base;
24        $attempt = 1;
25
26        while ($this->isTaken($candidate)) {
27            $candidate = $base . ++$attempt;
28        }
29
30        $this->taken[strtolower($candidate)] = true;
31
32        return $candidate;
33    }
34
35    public function claimExactly(string $name): void
36    {
37        if ($this->isReserved($name) || $this->isTaken($name)) {
38            throw new LogicException(\sprintf('%s is not free to claim.', $name));
39        }
40
41        $this->taken[strtolower($name)] = true;
42    }
43
44    public function isReserved(string $name): bool
45    {
46        return \in_array(strtolower($name), array_map(strtolower(...), $this->reserved), true);
47    }
48
49    private function isTaken(string $name): bool
50    {
51        return isset($this->taken[strtolower($name)]);
52    }
53}