Lines
100.00%
25 / 25
Methods
100.00%
6 / 6
Classes
100.00%
1 / 1
| Name | Lines | Methods | CRAP | ||||
|---|---|---|---|---|---|---|---|
| __construct | 100.00% | 2 / 2 | 100.00% | 1 / 1 | 1 | ||
| resolvePhpType | 100.00% | 4 / 4 | 100.00% | 1 / 1 | 3 | ||
| phpTypeForKind | 100.00% | 6 / 6 | 100.00% | 1 / 1 | 4 | ||
| phpTypeForConstant | 100.00% | 2 / 2 | 100.00% | 1 / 1 | 2 | ||
| phpTypeForSelect | 100.00% | 4 / 4 | 100.00% | 1 / 1 | 3 | ||
| selectOptionValues | 100.00% | 7 / 7 | 100.00% | 1 / 1 | 2 | ||
| 10 | final readonly class ServiceSelector | |
| 11 | { | |
| 12 | private const string ANY_ARRAY = 'array<array-key, mixed>'; | |
| 13 | ||
| 14 | private const string DURATION = 'array{days?: int|float, hours?: int|float, minutes?: int|float, seconds?: int|float, milliseconds?: int|float}|string|int|float'; | |
| 15 | ||
| 16 | /** @var array<string, array{0: string, 1?: string}> */ | |
| 17 | private const array KIND_TYPES = [ | |
| 18 | 'action' => ['array', self::ANY_ARRAY], | |
| 19 | 'addon' => ['string'], | |
| 20 | 'area' => ['string'], | |
| 21 | 'assist_pipeline' => ['string'], | |
| 22 | 'attribute' => ['string'], | |
| 23 | 'backup_location' => ['string'], | |
| 24 | 'boolean' => ['bool'], | |
| 25 | 'color_rgb' => ['array', 'array{int, int, int}'], | |
| 26 | 'color_temp' => ['int|float'], | |
| 27 | 'condition' => ['array', self::ANY_ARRAY], | |
| 28 | 'config_entry' => ['string'], | |
| 29 | 'conversation_agent' => ['string'], | |
| 30 | 'country' => ['string'], | |
| 31 | 'date' => ['string'], | |
| 32 | 'datetime' => ['string'], | |
| 33 | 'device' => ['string'], | |
| 34 | 'duration' => ['array|string|int|float', self::DURATION], | |
| 35 | 'entity' => ['string'], | |
| 36 | 'file' => ['string'], | |
| 37 | 'floor' => ['string'], | |
| 38 | 'icon' => ['string'], | |
| 39 | 'label' => ['string'], | |
| 40 | 'language' => ['string'], | |
| 41 | 'location' => ['array', self::ANY_ARRAY], | |
| 42 | 'media' => ['array', self::ANY_ARRAY], | |
| 43 | 'navigation' => ['string'], | |
| 44 | 'number' => ['int|float'], | |
| 45 | 'object' => ['array', self::ANY_ARRAY], | |
| 46 | 'qr_code' => ['string'], | |
| 47 | 'selector' => ['array', self::ANY_ARRAY], | |
| 48 | 'state' => ['string'], | |
| 49 | 'statistic' => ['string'], | |
| 50 | 'target' => ['array', self::ANY_ARRAY], | |
| 51 | 'template' => ['string'], | |
| 52 | 'text' => ['string|int|float'], | |
| 53 | 'theme' => ['string'], | |
| 54 | 'time' => ['string'], | |
| 55 | 'trigger' => ['array', self::ANY_ARRAY], | |
| 56 | 'tts' => ['string'], | |
| 57 | 'ui_action' => ['array', self::ANY_ARRAY], | |
| 58 | 'ui_color' => ['string'], | |
| 59 | ]; | |
| 60 | ||
| 61 | private const string SELECT = 'select'; | |
| 62 | ||
| 63 | private const string CONSTANT = 'constant'; | |
| 64 | ||
| 65 | private const string CONSTANT_VALUE = 'value'; | |
| 66 | ||
| 67 | public ?string $kind; | |
| 68 | ||
| 69 | public RawMap $configuration; | |
| 70 | ||
| 71 | public function __construct(RawMap $selector) | |
| 72 | { | |
| 73 | $this->kind = $selector->listKeys()[0] ?? null; | |
| 74 | $this->configuration = $selector->readMap($this->kind ?? ''); | |
| 75 | } | |
| 76 | ||
| 77 | public function resolvePhpType(): PhpType | |
| 78 | { | |
| 79 | if ($this->kind === null) { | |
| 80 | return PhpType::mixed(); | |
| 81 | } | |
| 82 | ||
| 83 | $type = $this->phpTypeForKind($this->kind); | |
| 84 | ||
| 85 | return $this->configuration->readBool('multiple') ? $type->listOf() : $type; | |
| 86 | } | |
| 87 | ||
| 88 | private function phpTypeForKind(string $kind): PhpType | |
| 89 | { | |
| 90 | if ($kind === self::SELECT) { | |
| 91 | return $this->phpTypeForSelect(); | |
| 92 | } | |
| 93 | ||
| 94 | if ($kind === self::CONSTANT) { | |
| 95 | return $this->phpTypeForConstant(); | |
| 96 | } | |
| 97 | ||
| 98 | $type = self::KIND_TYPES[$kind] ?? null; | |
| 99 | ||
| 100 | return $type === null ? PhpType::mixed() : PhpType::fromNative(...$type); | |
| 101 | } | |
| 102 | ||
| 103 | private function phpTypeForConstant(): PhpType | |
| 104 | { | |
| 105 | $value = $this->configuration->readScalar(self::CONSTANT_VALUE); | |
| 106 | ||
| 107 | return $value === null ? PhpType::mixed() : PhpType::fromNative(get_debug_type($value), var_export($value, true)); | |
| 108 | } | |
| 109 | ||
| 110 | private function phpTypeForSelect(): PhpType | |
| 111 | { | |
| 112 | $options = $this->selectOptionValues(); | |
| 113 | ||
| 114 | if ($options === [] || $this->configuration->readBool('custom_value')) { | |
| 115 | return PhpType::fromNative('string'); | |
| 116 | } | |
| 117 | ||
| 118 | return PhpType::fromNative('string', implode('|', array_map(static fn(string $option): string => var_export($option, true), $options))); | |
| 119 | } | |
| 120 | ||
| 121 | /** @return list<string> */ | |
| 122 | private function selectOptionValues(): array | |
| 123 | { | |
| 124 | $options = $this->configuration->listStrings('options'); | |
| 125 | ||
| 126 | if ($options !== []) { | |
| 127 | return $options; | |
| 128 | } | |
| 129 | ||
| 130 | return array_values(array_filter( | |
| 131 | array_map(static fn(RawMap $option): ?string => $option->readString('value'), $this->configuration->listMaps('options')), | |
| 132 | static fn(?string $value): bool => $value !== null, | |
| 133 | )); | |
| 134 | } | |
| 135 | } |