diff --git a/src/EnumBitMask.php b/src/EnumBitMask.php index a154f5c..c07a92f 100644 --- a/src/EnumBitMask.php +++ b/src/EnumBitMask.php @@ -13,7 +13,10 @@ final class EnumBitMask implements BitMaskInterface { private BitMask $bitmask; - /** @var array $map case => bit */ + + /** + * @var array $map case => bit + */ private array $map = []; /** @@ -33,39 +36,102 @@ public function __construct( $this->bitmask = new BitMask($mask, count($this->enum::cases()) - 1); } + /** + * Create an instance with given flags on + * + * @param class-string $enum + * + * @return static + */ + public static function create(string $enum, UnitEnum ...$bits): static + { + return (new static($enum))->set(...$bits); + } + + /** + * Create an instance with all flags on + * + * @param class-string $enum + * + * @return static + */ + public static function all(string $enum): static + { + return self::create($enum, ...$enum::cases()); + } + + /** + * Create an instance with no flags on + * + * @param class-string $enum + * + * @return static + */ + public static function none(string $enum): static + { + return self::create($enum); + } + + /** + * Create an instance without given flags on + * + * @param class-string $enum + * + * @return static + */ + public static function without(string $enum, UnitEnum ...$bits): static + { + return self::all($enum)->remove(...$bits); + } + public function get(): int { return $this->bitmask->get(); } - /** @throws UnknownEnumException|NotSingleBitException */ - public function set(UnitEnum ...$bits): void + /** + * @throws UnknownEnumException|NotSingleBitException + * + * @return $this + */ + 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): void + /** + * @throws UnknownEnumException|NotSingleBitException + * + * @return $this + */ + public function remove(UnitEnum ...$bits): self { $this->has(...$bits); $this->bitmask->remove(...$this->enumToInt(...$bits)); + + return $this; } - /** @throws UnknownEnumException|NotSingleBitException */ + /** + * @throws UnknownEnumException|NotSingleBitException + */ public function has(UnitEnum ...$bits): bool { array_walk( $bits, fn(UnitEnum $bit) => - $bit instanceof $this->enum ?: + $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[] */ + /** + * @return int[] + */ private function enumToInt(UnitEnum ...$bits): array { return array_map(fn(UnitEnum $bit) => $this->map[$bit->name], $bits); diff --git a/tests/EnumBitMaskTest.php b/tests/EnumBitMaskTest.php index afa9c97..6003c2c 100644 --- a/tests/EnumBitMaskTest.php +++ b/tests/EnumBitMaskTest.php @@ -100,4 +100,44 @@ public function testBackedEnum(): void assertFalse($backedIntEnumBitmask->has(BackedInt::Create, BackedInt::Read)); assertTrue($backedIntEnumBitmask->has(BackedInt::Update, BackedInt::Delete)); } + + public function testCreateFactory(): void + { + $enumBitmask = EnumBitMask::create(Permissions::class); + assertSame(0, $enumBitmask->get()); + + $enumBitmask = EnumBitMask::create(Permissions::class, Permissions::Create); + assertSame(1, $enumBitmask->get()); + + $enumBitmask = EnumBitMask::create(Permissions::class, Permissions::Delete); + assertSame(8, $enumBitmask->get()); + + $enumBitmask = EnumBitMask::create(Permissions::class, Permissions::Delete, Permissions::Create); + assertSame(9, $enumBitmask->get()); + + $enumBitmask = EnumBitMask::create(Permissions::class, Permissions::Create, Permissions::Delete); + assertSame(9, $enumBitmask->get()); + + + $this->expectException(UnknownEnumException::class); + EnumBitMask::create(Permissions::class, Unknown::Case); + } + + public function testNoneFactory(): void + { + $enumBitmask = EnumBitMask::none(Permissions::class); + assertSame(0, $enumBitmask->get()); + } + + public function testAllFactory(): void + { + $enumBitmask = EnumBitMask::all(Permissions::class); + assertSame(15, $enumBitmask->get()); + } + + public function testWithoutFactory(): void + { + $enumBitmask = EnumBitMask::without(Permissions::class, Permissions::Delete); + assertSame(7, $enumBitmask->get()); + } }