Lines 88.88% 16 / 18
Methods 66.66% 2 / 3
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 of 100.00% 5 / 5 100.00% 1 / 1 4
 held 100.00% 8 / 8 100.00% 1 / 1 4
 period 60.00% 3 / 5 0.00% 0 / 1 6.60
13final readonly class QuestMarks
14{
15    private const array FIELDS = ['id0', 'id1', 'id2', 'id3', 'id4', 'id5', 'id6', 'id7', 'iw0', 'im0'];
16
17    /** @return Option\Option<QuestMark> */
18    public static function of(string $field, int $index): Option\Option
19    {
20        $period = self::period($field);
21        $group = substr($field, offset: 2);
22        if ($period === null || $index < 0 || !ctype_digit($group)) {
23            return Option\none();
24        }
25
26        return Option\some(new QuestMark($period, (int) $group, $index));
27    }
28
29    /**
30     * @param array<array-key, mixed> $data
31     *
32     * @throws CoercionException
33     *
34     * @return list<QuestMark>
35     */
36    public static function held(array $data): array
37    {
38        $marks = [];
39        foreach (self::FIELDS as $field) {
40            $flags = Type\uint()->coerce($data[$field] ?? 0);
41            for ($index = 0; $index < 32; $index++) {
42                if ((($flags >> $index) & 1) !== 1) {
43                    continue;
44                }
45
46                $marks[] = self::of($field, $index)->unwrap();
47            }
48        }
49
50        return $marks;
51    }
52
53    private static function period(string $field): ?QuestPeriod
54    {
55        return match (substr($field, offset: 0, length: 2)) {
56            'id' => QuestPeriod::Day,
57            'iw' => QuestPeriod::Week,
58            'im' => QuestPeriod::Month,
59            default => null,
60        };
61    }
62}