diff --git a/src/CasesCollection.php b/src/CasesCollection.php index c50fbea..60fff23 100644 --- a/src/CasesCollection.php +++ b/src/CasesCollection.php @@ -5,17 +5,18 @@ use BackedEnum; use Countable; use IteratorAggregate; +use JsonSerializable; +use Stringable; use Traversable; /** * The collection of enum cases. * - * @template TKey of array-key * @template TValue * - * @implements IteratorAggregate + * @implements IteratorAggregate */ -class CasesCollection implements Countable, IteratorAggregate +class CasesCollection implements Countable, IteratorAggregate, JsonSerializable, Stringable { /** * Whether the cases belong to a backed enum. @@ -25,13 +26,31 @@ class CasesCollection implements Countable, IteratorAggregate /** * Instantiate the class. * - * @param array $cases + * @param array $cases */ - final public function __construct(protected array $cases) + final public function __construct(protected readonly array $cases) { $this->enumIsBacked = reset($cases) instanceof BackedEnum; } + /** + * Turn the collection into a string. + */ + public function __toString(): string + { + return (string) json_encode($this->jsonSerialize()); + } + + /** + * Turn the collection into a JSON serializable array. + * + * @return list + */ + public function jsonSerialize(): array + { + return $this->enumIsBacked ? $this->values() : $this->names(); + } + /** * Retrieve the count of cases. */ @@ -43,7 +62,7 @@ public function count(): int /** * Retrieve the iterable cases. * - * @return Traversable + * @return Traversable */ public function getIterator(): Traversable { @@ -53,24 +72,38 @@ public function getIterator(): Traversable /** * Retrieve all the cases as a plain array. * - * @return array + * @return array */ public function all(): array { return $this->cases; } + /** + * Determine whether the collection contains the given case. + */ + public function has(mixed $case): bool + { + foreach ($this->cases as $instance) { + if ($instance->is($case)) { + return true; + } + } + + return false; + } + /** * Retrieve all the cases as a plain array recursively. * - * @return array + * @return array */ public function toArray(): array { $array = []; foreach ($this->cases as $key => $value) { - $array[$key] = $value instanceof self ? $value->toArray() : $value; + $array[$key] = $value instanceof static ? $value->toArray() : $value; } return $array; @@ -79,7 +112,7 @@ public function toArray(): array /** * Retrieve the first case. * - * @param (callable(TValue, TKey): bool)|null $callback + * @param (callable(TValue, array-key): bool)|null $callback * @return ?TValue */ public function first(callable $callback = null): mixed @@ -144,8 +177,8 @@ public function pluck(callable|string $value, callable|string $key = null): arra * * @template TMapValue * - * @param callable(TValue, TKey): TMapValue $callback - * @return array + * @param callable(TValue, array-key): TMapValue $callback + * @return array */ public function map(callable $callback): array { diff --git a/src/Concerns/Compares.php b/src/Concerns/Compares.php index 4e4ba4c..5b8431c 100644 --- a/src/Concerns/Compares.php +++ b/src/Concerns/Compares.php @@ -26,13 +26,7 @@ public static function has(mixed $target): bool */ public static function doesntHave(mixed $target): bool { - foreach (self::cases() as $case) { - if ($case->is($target)) { - return false; - } - } - - return true; + return !self::has($target); } /** @@ -74,12 +68,6 @@ public function in(iterable $targets): bool */ public function notIn(iterable $targets): bool { - foreach ($targets as $target) { - if ($this->is($target)) { - return false; - } - } - - return true; + return !$this->in($targets); } } diff --git a/src/Concerns/SelfAware.php b/src/Concerns/SelfAware.php index 4fa8834..db42c81 100644 --- a/src/Concerns/SelfAware.php +++ b/src/Concerns/SelfAware.php @@ -31,6 +31,22 @@ public static function isBacked(): bool return is_subclass_of(self::class, BackedEnum::class); } + /** + * Determine whether the enum is backed by integer. + */ + public static function isBackedByInteger(): bool + { + return (new ReflectionEnum(self::class))->getBackingType()?->getName() === 'int'; + } + + /** + * Determine whether the enum is backed by string. + */ + public static function isBackedByString(): bool + { + return (new ReflectionEnum(self::class))->getBackingType()?->getName() === 'string'; + } + /** * Retrieve all the meta names of the enum. * @@ -41,12 +57,12 @@ public static function metaNames(): array $meta = []; $enum = new ReflectionEnum(self::class); - foreach ($enum->getAttributes(Meta::class) as $attribute) { + foreach ($enum->getAttributes(Meta::class, ReflectionAttribute::IS_INSTANCEOF) as $attribute) { array_push($meta, ...$attribute->newInstance()->names()); } foreach ($enum->getCases() as $case) { - foreach ($case->getAttributes(Meta::class) as $attribute) { + foreach ($case->getAttributes(Meta::class, ReflectionAttribute::IS_INSTANCEOF) as $attribute) { array_push($meta, ...$attribute->newInstance()->names()); } } diff --git a/src/Enums.php b/src/Enums.php index 1ddc0aa..3c0d781 100644 --- a/src/Enums.php +++ b/src/Enums.php @@ -7,7 +7,7 @@ use Closure; /** - * The global behavior for all enums. + * The enums manager. */ class Enums { @@ -35,31 +35,31 @@ class Enums /** * Set the logic to run when an inaccessible enum method is called. * - * @param Closure(class-string $enum, string $name, array $arguments): mixed $callback + * @param callable(class-string $enum, string $name, array $arguments): mixed $callback */ - public static function onStaticCall(Closure $callback): void + public static function onStaticCall(callable $callback): void { - static::$onStaticCall = $callback; + static::$onStaticCall = $callback(...); } /** * Set the logic to run when an inaccessible case method is called. * - * @param Closure(object $case, string $name, array $arguments): mixed $callback + * @param callable(object $case, string $name, array $arguments): mixed $callback */ - public static function onCall(Closure $callback): void + public static function onCall(callable $callback): void { - static::$onCall = $callback; + static::$onCall = $callback(...); } /** * Set the logic to run when a case is invoked. * - * @param Closure(object $case, mixed ...$arguments): mixed $callback + * @param callable(object $case, mixed ...$arguments): mixed $callback */ - public static function onInvoke(Closure $callback): void + public static function onInvoke(callable $callback): void { - static::$onInvoke = $callback; + static::$onInvoke = $callback(...); } /** diff --git a/tests/BackedEnumTest.php b/tests/BackedEnumTest.php index 74379ea..0d2545f 100644 --- a/tests/BackedEnumTest.php +++ b/tests/BackedEnumTest.php @@ -3,6 +3,7 @@ use Cerbero\Enum\CasesCollection; use Cerbero\Enum\BackedEnum; use Cerbero\Enum\Enums; +use Cerbero\Enum\PureEnum; use Pest\Expectation; it('determines whether the enum is pure') @@ -13,6 +14,13 @@ ->expect(BackedEnum::isBacked()) ->toBeTrue(); +it('determines whether the enum is backed by integer or string', function() { + expect(BackedEnum::isBackedByInteger())->toBeTrue(); + expect(BackedEnum::isBackedByString())->toBeFalse(); + expect(PureEnum::isBackedByInteger())->toBeFalse(); + expect(PureEnum::isBackedByString())->toBeFalse(); +}); + it('retrieves all the names of the cases') ->expect(BackedEnum::names()) ->toBe(['one', 'two', 'three']); diff --git a/tests/CasesCollectionTest.php b/tests/CasesCollectionTest.php index 54e7bd7..7dcaeb6 100644 --- a/tests/CasesCollectionTest.php +++ b/tests/CasesCollectionTest.php @@ -5,11 +5,41 @@ use Cerbero\Enum\PureEnum; use Pest\Expectation; +it('turns into a JSON with pure cases', function() { + expect((string) new CasesCollection(PureEnum::cases())) + ->toBe('["one","two","three"]'); +}); + +it('turns into a JSON with backed cases', function() { + expect((string) new CasesCollection(BackedEnum::cases())) + ->toBe('[1,2,3]'); +}); + it('retrieves all the cases') ->expect(new CasesCollection(PureEnum::cases())) ->all() ->toBe([PureEnum::one, PureEnum::two, PureEnum::three]); +it('determines whether a pure collection contains an item', function() { + $collection = new CasesCollection(PureEnum::cases()); + + expect($collection->has(PureEnum::one))->toBeTrue(); + expect($collection->has('one'))->toBeTrue(); + expect($collection->has(BackedEnum::one))->toBeFalse(); + expect($collection->has('four'))->toBeFalse(); + expect($collection->has(1))->toBeFalse(); +}); + +it('determines whether a backed collection contains an item', function() { + $collection = new CasesCollection(BackedEnum::cases()); + + expect($collection->has(BackedEnum::one))->toBeTrue(); + expect($collection->has(1))->toBeTrue(); + expect($collection->has(PureEnum::one))->toBeFalse(); + expect($collection->has(4))->toBeFalse(); + expect($collection->has('one'))->toBeFalse(); +}); + it('retrieves the count of all the cases') ->expect(new CasesCollection(PureEnum::cases())) ->count()