diff --git a/CHANGELOG.md b/CHANGELOG.md index 153d2a2..ec28a7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to `Enumhancer` will be documented in this file +## 1.5.0 - 2022-05-31 +- added `Extractor` to extract enums from a string mentioned by value +- some documentation repairs + ## 1.4.1 - 2022-03-04 - added `cases` method to `Subset` @@ -15,7 +19,7 @@ All notable changes to `Enumhancer` will be documented in this file ## 1.3.0 - 2022-02-28 -- added Multi. Currently allows you to compare against a subset of your enum. +- added Multi. Currently allows you to compare against a subset of your enum ## 1.2.0 - 2022-02-26 diff --git a/README.md b/README.md index 06c4d84..9f335a7 100644 --- a/README.md +++ b/README.md @@ -3,24 +3,27 @@ [![Latest Version on Packagist](https://img.shields.io/packagist/v/henzeb/enumhancer.svg?style=flat-square)](https://packagist.org/packages/henzeb/enumhancer) [![Total Downloads](https://img.shields.io/packagist/dt/henzeb/enumhancer.svg?style=flat-square)](https://packagist.org/packages/henzeb/enumhancer) -In this library you'll find some of the most common use-cases for enums. -If you find yourself recreating functionalities, maybe this package is -something for you. +This package is your Swiss Army knife when it comes to PHP 8.1's enums. +In this package you will find a lot of tools for the most common use cases +and more will be added in the future. + +If you have an idea or you miss something that needs to be added, +just let me know. This package currently supports the following: - Constructor (in case you're migrating from [Spatie's PHP Enum](https://github.com/spatie/enum)) - Comparison +- Extractor - From (for unit enums) -- Make (Ability to make from enum-name) - Labels +- Make (Ability to make from enum-name) - Mappers -- Multi - Properties - Reporting (Logging) -- Value - +- Subset +- Value (for unit enums) ## Installation @@ -48,18 +51,19 @@ You can also just use one of the functionalities by using the specific trait for that functionality. Note: all traits can be used next to each other, except for `Mappers`, which has -implemented his own version of `Makers` and `Reporters`. +implemented the methods of `Makers`, `Extractor` and `Reporters`. ### Functionality - [Constructor](docs/constructor.md) - [Comparison](docs/comparison.md) +- [Extractor](docs/extractor.md) - [From](docs/from.md) - [Labels](docs/labels.md) - [Makers](docs/makers.md) - [Mappers](docs/mappers.md) -- [Subset](docs/subset.md) - [Properties](docs/properties.md) - [Reporters](docs/reporters.md) +- [Subset](docs/subset.md) - [Value](docs/value.md) ### Laravel diff --git a/composer.json b/composer.json index 3fec733..5c5cf46 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,13 @@ "description": "Enrich your enums with common functionality", "keywords": [ "henzeb", - "enumhancer" + "enumhancer", + "enums", + "enum", + "enumerators", + "enumerator", + "mappers", + "comparison" ], "homepage": "https://github.com/henzeb/enumhancer", "license": "AGPL-3.0-only", diff --git a/docs/extractor.md b/docs/extractor.md new file mode 100644 index 0000000..cda7e9a --- /dev/null +++ b/docs/extractor.md @@ -0,0 +1,26 @@ +# Extractor +Sometimes you have a piece of text that contains an enum value that you +want to use. With this you can simply pass a multiline string and extract all +enums that are mentioned by value. + +Note: This also works with `Mappers`. + +## Usage +```php +use Henzeb\Enumhancer\Concerns\Extractor; + +enum YourEnum: string { + use Extractor; + + case ENUM = 'enum'; + case ENUM2 = 'another enum'; +} +``` + +### Examples +```php +YourEnum::extract('you can find another enum here'); // returns [YourEnum::ENUM2] +YourEnum::extract('A lot of text with (enum)'); // returns [YourEnum::ENUM] +YourEnum::extract('another enum (enum)'); // returns [YourEnum::ENUM2, YourEnum::ENUM] +YourEnum::extract('extact case sensitive another ENUM') // returns [YourEnum::ENUM2]; +``` diff --git a/docs/from.md b/docs/from.md index 7c2aaaf..b37e5f6 100644 --- a/docs/from.md +++ b/docs/from.md @@ -1,9 +1,9 @@ # From -The methods `from` and `tryFrom` do not exist on non-backed enums. When you have -a non-backed enum where the string value is exactly the same as it's key, but -you need `from` or `tryFrom` because of some library (like Laravel's validation), -this might be useful. +The methods `from` and `tryFrom` do not exist on non-backed enums. When you +have a non-backed enum where the string value is exactly the same as it's key, +but you need `from` or `tryFrom` because of some library (like Laravel's +validation), this might be useful. ## Usage diff --git a/docs/mappers.md b/docs/mappers.md index 059f352..df611ce 100644 --- a/docs/mappers.md +++ b/docs/mappers.md @@ -5,7 +5,7 @@ for example are building against multiple third party API's and you need to translate the enums between one and other. Note: `Mappers` is the only one that you cannot use together with -`Makers` and `Reporters`. This is simply due to the fact that it is +`Makers`, `Extractor` and `Reporters`. This is simply due to the fact that it is implementing the methods for those by itself. Under the hood it's using their functionality, so the methods will work just the same. @@ -62,11 +62,16 @@ YourEnum::makeArray(['unknown', 'NOT_MAPPED'], new YourMapper()); // will throw YourEnum::tryMakeArray(['Mapped', 'NOT_MAPPED'], YourMapper::class); // will return [YourEnum::ENUM, YourEnum::NOT_MAPPED] YourEnum::tryMakeArray(['unknown', 'NOT_MAPPED'], new YourMapper()); // will return [YourEnum::NOT_MAPPED] -/** tryMakeArray */ +/** makeOrReport */ YourEnum::makeOrReport(['Mapped', 'NOT_MAPPED'], YourMapper::class); // will return [YourEnum::ENUM, YourEnum::NOT_MAPPED] YourEnum::makeOrReport(['unknown', 'NOT_MAPPED'], new YourMapper()); // will return [YourEnum::NOT_MAPPED] + +/** extract */ +YourEnum::extract('a sentence with Mapped in it', YourMapper::class); // will return [YourEnum::ENUM] +YourEnum::extract('a sentence with Mapped in it', new YourMapper()); // will return [YourEnum::ENUM] ``` Note: See for the `makeOrReport` method: [Reporters](reporters.md) +Note: See for the `extract` method: [Extractor](extractor.md) ### Shared Mapper @@ -93,7 +98,8 @@ And then use the commands as shown in the `example` section. ## The mapper method -In case you don't want to add the mapper all the time, you can also specify a `mapper` method that returns your mapper. +In case you don't want to add the mapper all the time, you can also specify +a `mapper` method that returns your mapper. ```php use Henzeb\Enumhancer\Concerns\Mappers; diff --git a/src/Concerns/Extractor.php b/src/Concerns/Extractor.php new file mode 100644 index 0000000..4a388ee --- /dev/null +++ b/src/Concerns/Extractor.php @@ -0,0 +1,13 @@ +map($value, static::class) ?? $value - : $value; - - return ($mapper = self::mapper()) ? - $mapper->map($value, static::class) ?? $value - : $value; - } - final public static function make(string|int|null $value, Mapper|string $mapper = null): self { - return EnumMakers::make(self::class, self::map($value, $mapper)); + return EnumMakers::make(self::class, EnumMapper::map($value, $mapper, self::mapper())); } final public static function tryMake(string|int|null $value, Mapper|string $mapper = null): ?self { - return EnumMakers::tryMake(self::class, self::map($value, $mapper)); + return EnumMakers::tryMake(self::class, EnumMapper::map($value, $mapper, self::mapper())); } final public static function makeArray(iterable $values, Mapper|string $mapper = null): array { - $mapped = []; - foreach($values as $value) { - $mapped[] = self::map($value, $mapper); - } - return EnumMakers::makeArray(self::class, $mapped); + + return EnumMakers::makeArray( + self::class, + EnumMapper::mapArray($values, $mapper, self::mapper()) + ); } final public static function tryMakeArray(iterable $values, Mapper|string $mapper = null): array { - $mapped = []; - foreach($values as $value) { - $mapped[] = self::map($value, $mapper); - } - return EnumMakers::tryMakeArray(self::class, $mapped); + return EnumMakers::tryMakeArray( + self::class, + EnumMapper::mapArray($values, $mapper, self::mapper()) + ); } - final public static function makeOrReport(int|string|null $values, BackedEnum $context = null, Mapper $mapper = null): ?self + final public static function makeOrReport(int|string|null $value, BackedEnum $context = null, Mapper|string $mapper = null): ?self { - return EnumReporter::makeOrReport(self::class, self::map($values, $mapper), $context, self::reporter()); + return EnumReporter::makeOrReport(self::class, EnumMapper::map($value, $mapper, self::mapper()), $context, self::reporter()); } - public static function makeOrReportArray(iterable $values, BackedEnum $context = null, Mapper $mapper = null): array + public static function makeOrReportArray(iterable $values, BackedEnum $context = null, Mapper|string $mapper = null): array { - $mapped = []; - foreach($values as $value) { - $mapped[] = self::map($value, $mapper); - } - - return EnumReporter::makeOrReportArray(self::class, $mapped, $context, self::reporter()); + return EnumReporter::makeOrReportArray( + self::class, + EnumMapper::mapArray($values, $mapper, self::mapper()), + $context, + self::reporter() + ); } + public static function extract(string $text, Mapper|string $mapper = null): array + { + $mappers = array_filter( + [ + is_string($mapper) ? new $mapper() : $mapper, self::mapper() + ] + ); + + return EnumExtractor::extract(self::class, $text, ...$mappers); + } } diff --git a/src/Contracts/Mapper.php b/src/Contracts/Mapper.php index 1fccfbd..7a504ff 100644 --- a/src/Contracts/Mapper.php +++ b/src/Contracts/Mapper.php @@ -14,7 +14,7 @@ private function parse(mixed $value): ?string return null; } - if(empty($value)) { + if (empty($value)) { return null; } @@ -40,4 +40,22 @@ public function defined(string $key, string $prefix = null): bool { return (bool)$this->map($key, $prefix); } + + public function keys(string $prefix = null): array + { + $mappable = $this->mappable(); + + return array_merge( + array_keys( + + array_filter( + $mappable, + function ($value) { + return !is_array($value); + } + ), + ), + is_array($mappable[$prefix] ?? null) ? $mappable[$prefix] : [] + ); + } } diff --git a/src/Helpers/EnumExtractor.php b/src/Helpers/EnumExtractor.php new file mode 100644 index 0000000..b2d1bf8 --- /dev/null +++ b/src/Helpers/EnumExtractor.php @@ -0,0 +1,45 @@ +value; + } + + return $enum->name; + }, $class::cases() + ); + + $match = implode( + '|', + array_merge( + $match, + ...array_map(fn(Mapper $map) => $map->keys($class), $mappers) + )); + + preg_match_all(sprintf('/%s/i', $match), $text, $matches); + + $matches = array_map(fn($value) => EnumMapper::map($value, ...$mappers), $matches[0] ?? []); + + return EnumMakers::makeArray($class, $matches); + } +} diff --git a/src/Helpers/EnumMapper.php b/src/Helpers/EnumMapper.php new file mode 100644 index 0000000..363addb --- /dev/null +++ b/src/Helpers/EnumMapper.php @@ -0,0 +1,33 @@ +map($value, static::class) ?? $value; + } + + return $value; + } + + public static function mapArray(iterable $values, Mapper|string|null ...$mappers): array + { + $mapped = []; + + foreach ($values as $value) { + $mapped[] = EnumMapper::map($value, ...$mappers); + } + + return $mapped; + } +} diff --git a/tests/Fixtures/ExtractBackedEnum.php b/tests/Fixtures/ExtractBackedEnum.php new file mode 100644 index 0000000..fbf4d77 --- /dev/null +++ b/tests/Fixtures/ExtractBackedEnum.php @@ -0,0 +1,18 @@ +assertEquals( + [ + ExtractBackedEnum::AN_ENUM + ], + ExtractBackedEnum::extract('this is an enum test text') + ); + } + + public function testShouldFindEnumInTextCaseInsensitive() + { + $this->assertEquals( + [ + ExtractBackedEnum::AN_ENUM + ], + ExtractBackedEnum::extract('this is an ENUM test text') + ); + } + + public function testShouldNotFindEnumInText() + { + $this->assertEquals( + [], + ExtractBackedEnum::extract('This text contains nothing') + ); + } + + public function testShouldFindMultipleEnumInText() + { + $this->assertEquals( + [ + ExtractBackedEnum::AN_ENUM, + ExtractBackedEnum::ANOTHER_ENUM + ], + ExtractBackedEnum::extract('this is an enum test and this is another enum text') + ); + } + + public function testUniqueMultipleEnumsInText() + { + $this->assertEquals( + [ + ExtractBackedEnum::AN_ENUM, + ExtractBackedEnum::AN_ENUM, + ExtractBackedEnum::AN_ENUM + ], + ExtractBackedEnum::extract('an enum An ENUM an enum') + ); + } + + public function testExtractionWithUnitEnum() + { + $this->assertEquals( + [ + SubsetUnitEnum::ENUM, + SubsetUnitEnum::ENUM, + SubsetUnitEnum::ENUM, + ], + SubsetUnitEnum::extract('an enum An ENUM an EnUm') + ); + } +} diff --git a/tests/Unit/Concerns/MappersTest.php b/tests/Unit/Concerns/MappersTest.php index bf62259..bf83ef2 100644 --- a/tests/Unit/Concerns/MappersTest.php +++ b/tests/Unit/Concerns/MappersTest.php @@ -149,7 +149,7 @@ public function testMakeArrayShouldMapWithoutMapperGiven() public function testMakeArrayShouldThrowErrorWitMapper() { $this->expectError(); - EnhancedBackedEnum::makeArray(['ENUM','doesNotExist'], $this->getMapper()); + EnhancedBackedEnum::makeArray(['ENUM', 'doesNotExist'], $this->getMapper()); } public function testTryMakeArrayShouldWorkWithoutMapper() @@ -169,7 +169,7 @@ public function testTryMakeArrayShouldWorkWitMapper() { $this->assertEquals( [EnhancedBackedEnum::ENUM], - EnhancedBackedEnum::tryMakeArray(['mappedEnum','DoesNotExist'], $this->getMapper()) + EnhancedBackedEnum::tryMakeArray(['mappedEnum', 'DoesNotExist'], $this->getMapper()) ); } @@ -181,10 +181,39 @@ public function testTryMakeArrayShouldMapWithoutMapperGiven() ); } - public function testShouldUseMapperWhenConstructorIsUsed() { + public function testShouldUseMapperWhenConstructorIsUsed() + { $this->assertEquals( EnhancedBackedEnum::ENUM, EnhancedBackedEnum::anotherMappedEnum() ); } + + public function testShouldExtractWithDefaultMappedKey() + { + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::extract('This text contains anotherMappedEnum for you') + ); + } + + public function testShouldExtractWithMappedKey() + { + $this->assertEquals( + [EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::extract('This text contains mappedEnum for you', $this->getMapper()) + ); + } + + public function testShouldExtractWithMappedKeyAndDefaultMappedKey() + { + $this->assertEquals( + [EnhancedBackedEnum::ENUM, EnhancedBackedEnum::ENUM], + EnhancedBackedEnum::extract( + 'This text contains mappedEnum and anotherMappedEnum for you', + $this->getMapper() + ) + ); + } + } diff --git a/tests/Unit/Contracts/MapperTest.php b/tests/Unit/Contracts/MapperTest.php index d6733a0..46404bf 100644 --- a/tests/Unit/Contracts/MapperTest.php +++ b/tests/Unit/Contracts/MapperTest.php @@ -151,5 +151,20 @@ public function testIsDefinedWithoutPrefixWhileGiven() ); } + public function testReturnsKeys() + { + $this->assertEquals( + $this->getMapper(['defined' => 'this is defined'])->keys(), + ['defined'] + ); + } + + public function testReturnsKeysWithPrefix() + { + $this->assertEquals( + $this->getMapper(['defined' => 'this is defined', 'a_prefix'=>['prefixed_key']])->keys('a_prefix'), + ['defined', 'prefixed_key'] + ); + } }