Skip to content

Commit

Permalink
Add factory methods to create an enum bit mask (#31)
Browse files Browse the repository at this point in the history
* Add factory methods to create an enum bit mask

* Add a method to create a bit mask using one or multiple cases
* Add a method to create a bit mask using all cases
* Add a method to create a bit mask with no flags on
* Add a method to create a bit mask without some flags

* Psalm suppress mixed in factory, updated docblock

* Updated README

---------

Co-authored-by: yaroslavche <[email protected]>
  • Loading branch information
gisostallenberg and yaroslavche authored Jun 21, 2024
1 parent 4c59a8b commit 75d1b1b
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 4 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ $bitmask->set(Permissions::EXECUTE);
$bitmask->set(Unknown::Case); // throws an exception, only Permissions cases available
```

`EnumBitMask` have a factory methods:
```php
# Create a bit mask using one or multiple enum cases
$bitmask = EnumBitMask::create(Permissions::class, Permissions::EXECUTE);

# Create a bit mask using all enum cases
$bitmask = EnumBitMask::all(Permissions::class);

# Create a bit mask with no flags on (equivalent to create with no additional flags)
$bitmask = EnumBitMask::none(Permissions::class);

# Create a bit mask without specific flags
$bitmask = EnumBitMask::without(Permissions::class, Permissions::EXECUTE);
```

Exists [Bits](/src/Util/Bits.php) helper with static methods:

```php
Expand Down Expand Up @@ -103,6 +118,12 @@ $ ./vendor/bin/phpbench run benchmarks --report=default
$ composer phpstan
$ ./vendor/bin/phpstan analyse src/ -c phpstan.neon --level=9 --no-progress -vvv --memory-limit=1024M
```

##### Psalm
```bash
$ composer psalm
$ ./vendor/bin/psalm
```
##### PHP-CS
###### Code style check
```bash
Expand Down
63 changes: 59 additions & 4 deletions src/EnumBitMask.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
final class EnumBitMask implements BitMaskInterface
{
private BitMask $bitmask;

/** @var array<string, int> $map case => bit */
private array $map = [];

Expand All @@ -28,31 +29,85 @@ public function __construct(
throw new UnknownEnumException('EnumBitMask enum must be subclass of UnitEnum');
}
foreach ($this->enum::cases() as $index => $case) {
$this->map[strval($case->name)] = Bits::indexToBit($index);
$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): void
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
public function remove(UnitEnum ...$bits): self
{
$this->has(...$bits);
$this->bitmask->remove(...$this->enumToInt(...$bits));

return $this;
}

/** @throws UnknownEnumException|NotSingleBitException */
/**
* @psalm-suppress PossiblyUnusedReturnValue
* @throws UnknownEnumException|NotSingleBitException
*/
public function has(UnitEnum ...$bits): bool
{
array_walk(
Expand Down
40 changes: 40 additions & 0 deletions tests/EnumBitMaskTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,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());
}
}

0 comments on commit 75d1b1b

Please sign in to comment.