Lines 100.00% 18 / 18
Functions and Methods 100.00% 14 / 14
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
TypedCollection 100.00% 18 / 18 100.00% 14 / 14 16 100.00% 1 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 empty 100.00% 1 / 1 100.00% 1 / 1 1
 count 100.00% 1 / 1 100.00% 1 / 1 1
 isEmpty 100.00% 1 / 1 100.00% 1 / 1 1
 getFirst 100.00% 2 / 2 100.00% 1 / 1 2
 getLast 100.00% 2 / 2 100.00% 1 / 1 2
 listValues 100.00% 1 / 1 100.00% 1 / 1 1
 filter 100.00% 1 / 1 100.00% 1 / 1 1
 mapToList 100.00% 1 / 1 100.00% 1 / 1 1
 containsWhere 100.00% 1 / 1 100.00% 1 / 1 1
 findFirstWhere 100.00% 1 / 1 100.00% 1 / 1 1
 sortedBy 100.00% 3 / 3 100.00% 1 / 1 1
 getIterator 100.00% 1 / 1 100.00% 1 / 1 1
 arrangeElements n/a 0 / 0 n/a 0 / 0 0
 elements 100.00% 1 / 1 100.00% 1 / 1 1
1<?php
2
3declare(strict_types=1);
4
5namespace Stewart\Contracts\Collection;
6
7use ArrayIterator;
8use Closure;
9use Countable;
10use IteratorAggregate;
11use Traversable;
12
13/**
14 * @template TKey of array-key
15 * @template T
16 * @implements IteratorAggregate<TKey, T>
17 */
18abstract readonly class TypedCollection implements IteratorAggregate, Countable
19{
20    /** @param array<TKey, T> $elements */
21    final protected function __construct(private array $elements = []) {}
22
23    public static function empty(): static
24    {
25        return new static();
26    }
27
28    public function count(): int
29    {
30        return \count($this->elements);
31    }
32
33    public function isEmpty(): bool
34    {
35        return $this->elements === [];
36    }
37
38    /** @return T|null */
39    public function getFirst(): mixed
40    {
41        $key = array_key_first($this->elements);
42
43        return $key === null ? null : $this->elements[$key];
44    }
45
46    /** @return T|null */
47    public function getLast(): mixed
48    {
49        $key = array_key_last($this->elements);
50
51        return $key === null ? null : $this->elements[$key];
52    }
53
54    /** @return list<T> */
55    public function listValues(): array
56    {
57        return array_values($this->elements);
58    }
59
60    /** @param Closure(T): bool $predicate */
61    public function filter(Closure $predicate): static
62    {
63        return new static($this->arrangeElements(array_filter($this->elements, $predicate)));
64    }
65
66    /**
67     * @template TResult
68     * @param Closure(T): TResult $transform
69     * @return list<TResult>
70     */
71    public function mapToList(Closure $transform): array
72    {
73        return array_values(array_map($transform, $this->elements));
74    }
75
76    /** @param Closure(T): bool $predicate */
77    public function containsWhere(Closure $predicate): bool
78    {
79        return array_any($this->elements, $predicate);
80    }
81
82    /**
83     * @param Closure(T): bool $predicate
84     * @return T|null
85     */
86    public function findFirstWhere(Closure $predicate): mixed
87    {
88        return array_find($this->elements, $predicate);
89    }
90
91    /** @param Closure(T, T): int $comparator */
92    public function sortedBy(Closure $comparator): static
93    {
94        $sorted = $this->elements;
95        uasort($sorted, $comparator);
96
97        return new static($this->arrangeElements($sorted));
98    }
99
100    /** @return Traversable<TKey, T> */
101    public function getIterator(): Traversable
102    {
103        return new ArrayIterator($this->elements);
104    }
105
106    /**
107     * @param array<TKey, T> $elements
108     * @return array<TKey, T>
109     */
110    abstract protected function arrangeElements(array $elements): array;
111
112    /** @return array<TKey, T> */
113    protected function elements(): array
114    {
115        return $this->elements;
116    }
117}