Lines 100.00% 28 / 28
Functions and Methods 100.00% 8 / 8
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
TriggerSpec 100.00% 28 / 28 100.00% 8 / 8 18 100.00% 1 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 fromSpec 100.00% 6 / 6 100.00% 1 / 1 5
 getSharingKey 100.00% 1 / 1 100.00% 1 / 1 1
 hasSameTriggersAs 100.00% 1 / 1 100.00% 1 / 1 1
 listPlatforms 100.00% 1 / 1 100.00% 1 / 1 1
 listTriggerConfigs 100.00% 1 / 1 100.00% 1 / 1 1
 parseTriggers 100.00% 8 / 8 100.00% 1 / 1 4
 parseVariables 100.00% 9 / 9 100.00% 1 / 1 4
1<?php
2
3declare(strict_types=1);
4
5namespace Stewart\Support\Wire;
6
7use Stewart\Contracts\Exception\TriggerError;
8use Stewart\Contracts\Exception\TriggerException;
9use Stewart\Contracts\Trigger\Collection\HaTriggerCollection;
10use Stewart\Contracts\Trigger\HaTrigger;
11use Stewart\Contracts\Trigger\TriggerJson;
12
13/** @internal */
14final readonly class TriggerSpec
15{
16    /** @param array<string, mixed> $variables */
17    private function __construct(
18        public HaTriggerCollection $triggers,
19        public array $variables,
20    ) {}
21
22    /**
23     * @param HaTrigger|HaTriggerCollection|array<array-key, mixed> $trigger
24     * @param array<array-key, mixed> $variables
25     * @throws TriggerException
26     */
27    public static function fromSpec(HaTrigger|HaTriggerCollection|array $trigger, array $variables = []): self
28    {
29        $triggers = match (true) {
30            $trigger instanceof HaTrigger => HaTriggerCollection::fromList([$trigger]),
31            $trigger instanceof HaTriggerCollection => $trigger,
32            default => self::parseTriggers($trigger),
33        };
34
35        if ($triggers->isEmpty()) {
36            throw TriggerException::createForReason(TriggerError::ListEmpty);
37        }
38
39        return new self($triggers, self::parseVariables($variables));
40    }
41
42    public function getSharingKey(): string
43    {
44        return hash('xxh128', TriggerJson::encodeCanonically([$this->listTriggerConfigs(), $this->variables]));
45    }
46
47    public function hasSameTriggersAs(self $other): bool
48    {
49        return TriggerJson::encodeCanonically($this->listTriggerConfigs()) === TriggerJson::encodeCanonically($other->listTriggerConfigs());
50    }
51
52    /** @return list<string> */
53    public function listPlatforms(): array
54    {
55        return $this->triggers->mapToList(static fn(HaTrigger $trigger): string => $trigger->getPlatform());
56    }
57
58    /** @return list<array<string, mixed>> */
59    public function listTriggerConfigs(): array
60    {
61        return $this->triggers->mapToList(static fn(HaTrigger $trigger): array => $trigger->toArray());
62    }
63
64    /**
65     * @param array<array-key, mixed> $trigger
66     * @throws TriggerException
67     */
68    private static function parseTriggers(array $trigger): HaTriggerCollection
69    {
70        if (!array_is_list($trigger)) {
71            return HaTriggerCollection::fromList([HaTrigger::fromArray($trigger)]);
72        }
73
74        $triggers = [];
75
76        foreach ($trigger as $config) {
77            if (!\is_array($config)) {
78                throw TriggerException::configInvalid();
79            }
80
81            $triggers[] = HaTrigger::fromArray($config);
82        }
83
84        return HaTriggerCollection::fromList($triggers);
85    }
86
87    /**
88     * @param array<array-key, mixed> $variables
89     * @return array<string, mixed>
90     * @throws TriggerException
91     */
92    private static function parseVariables(array $variables): array
93    {
94        $typedVariables = [];
95
96        foreach ($variables as $name => $value) {
97            if (!\is_string($name)) {
98                throw TriggerException::createForReason(TriggerError::VariablesNotMap);
99            }
100
101            $typedVariables[$name] = $value;
102        }
103
104        $unencodablePath = TriggerJson::findUnencodablePath($typedVariables, 'variables');
105
106        if ($unencodablePath !== null) {
107            throw TriggerException::configUnencodable($unencodablePath);
108        }
109
110        return $typedVariables;
111    }
112}