-
Notifications
You must be signed in to change notification settings - Fork 6
/
EnumBitMask.php
128 lines (111 loc) · 3.43 KB
/
EnumBitMask.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<?php
declare(strict_types=1);
namespace BitMask;
use BitMask\Exception\NotSingleBitException;
use BitMask\Exception\UnknownEnumException;
use BitMask\Util\Bits;
use UnitEnum;
/** @psalm-suppress UnusedClass */
final class EnumBitMask implements BitMaskInterface
{
private BitMask $bitmask;
/** @var array<string, int> $map case => bit */
private array $map = [];
/**
* @param class-string $enum
* @throws UnknownEnumException
*/
public function __construct(
private readonly string $enum,
int $mask = 0,
) {
if (!is_subclass_of($this->enum, UnitEnum::class)) {
throw new UnknownEnumException('EnumBitMask enum must be subclass of UnitEnum');
}
foreach ($this->enum::cases() as $index => $case) {
$this->map[$case->name] = Bits::indexToBit($index);
}
$this->bitmask = new BitMask($mask, count($this->enum::cases()) - 1);
}
/**
* Create an instance with given flags on
*
* @param class-string $enum
* @throws UnknownEnumException|NotSingleBitException
*/
public static function create(string $enum, UnitEnum ...$bits): self
{
return (new EnumBitMask($enum))->set(...$bits);
}
/**
* Create an instance with all flags on
*
* @psalm-suppress MixedMethodCall, MixedArgument
* @param class-string $enum
* @throws UnknownEnumException|NotSingleBitException
*/
public static function all(string $enum): self
{
return self::create($enum, ...$enum::cases());
}
/**
* Create an instance with no flags on
*
* @psalm-suppress PossiblyUnusedMethod
* @param class-string $enum
* @throws UnknownEnumException|NotSingleBitException
*/
public static function none(string $enum): self
{
return self::create($enum);
}
/**
* Create an instance without given flags on
*
* @psalm-suppress PossiblyUnusedMethod
* @param class-string $enum
* @throws UnknownEnumException|NotSingleBitException
*/
public static function without(string $enum, UnitEnum ...$bits): self
{
return self::all($enum)->remove(...$bits);
}
public function get(): int
{
return $this->bitmask->get();
}
/** @throws UnknownEnumException|NotSingleBitException */
public function set(UnitEnum ...$bits): self
{
$this->has(...$bits);
$this->bitmask->set(...$this->enumToInt(...$bits));
return $this;
}
/** @throws UnknownEnumException|NotSingleBitException */
public function remove(UnitEnum ...$bits): self
{
$this->has(...$bits);
$this->bitmask->remove(...$this->enumToInt(...$bits));
return $this;
}
/**
* @psalm-suppress PossiblyUnusedReturnValue
* @throws UnknownEnumException|NotSingleBitException
*/
public function has(UnitEnum ...$bits): bool
{
array_walk(
$bits,
fn(UnitEnum $bit) =>
$bit instanceof $this->enum ?:
throw new UnknownEnumException(sprintf('Expected %s enum, %s provided', $this->enum, $bit::class))
);
/** @psalm-var UnitEnum[] $bits */
return $this->bitmask->has(...$this->enumToInt(...$bits));
}
/** @return int[] */
private function enumToInt(UnitEnum ...$bits): array
{
return array_map(fn(UnitEnum $bit) => $this->map[$bit->name], $bits);
}
}