Skip to content

Methods for individual enums #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 43 additions & 10 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,64 @@ jobs:
strategy:
fail-fast: true
matrix:
php: [ 8.1, 8.2, 8.3 ]
laravel: [ 10.*, 11.* ]
php: [ 8.1, 8.2, 8.3, 8.4 ]
laravel: [ 10.*, 11.*, 12.* ]
include:
- php: 8.1
laravel: 10.*
pest: 2.*
testbench: 8.*
larastan: 2.*
- php: 8.2
laravel: 10.*
pest: 2.*
testbench: 8.*
larastan: 2.*
- php: 8.3
laravel: 10.*
pest: 2.*
testbench: 8.*
larastan: 2.*
- php: 8.4
laravel: 10.*
pest: 2.*
testbench: 8.*
larastan: 2.*
- php: 8.2
laravel: 11.*
pest: 3.*
testbench: 9.*
larastan: 2.*
- php: 8.3
laravel: 11.*
pest: 3.*
testbench: 9.*
larastan: 2.*
- php: 8.4
laravel: 11.*
pest: 3.*
testbench: 9.*
larastan: 2.*
- php: 8.2
laravel: 12.*
pest: 3.*
testbench: 10.*
larastan: 3.*
- php: 8.3
laravel: 12.*
pest: 3.*
testbench: 10.*
larastan: 3.*
- php: 8.4
laravel: 12.*
pest: 3.*
testbench: 10.*
larastan: 3.*
exclude:
- php: 8.1
laravel: 11.*
- php: 8.1
laravel: 12.*

name: Paragon Tests - PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }}

Expand All @@ -45,7 +77,7 @@ jobs:
uses: actions/checkout@v4

- name: Cache dependencies
uses: actions/cache@v1
uses: actions/cache@v4
with:
path: ~/.composer/cache/files
key: dependencies-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
Expand All @@ -61,9 +93,10 @@ jobs:
- name: Install dependencies
run: |
composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update
composer require "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update --dev
composer require "pestphp/pest:${{ matrix.pest }}" "pestphp/pest-plugin-laravel:${{ matrix.pest }}" "pestphp/pest-plugin-type-coverage:${{ matrix.pest }}" --no-interaction --no-update --dev
composer update --prefer-dist --no-interaction --no-suggest --dev
composer require "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update
composer require "pestphp/pest:${{ matrix.pest }}" "pestphp/pest-plugin-laravel:${{ matrix.pest }}" "pestphp/pest-plugin-type-coverage:${{ matrix.pest }}" --no-interaction --no-update
composer require "larastan/larastan:${{ matrix.larastan }}" --no-interaction --no-update
composer update --prefer-dist --no-interaction
composer dump

- name: Execute tests
Expand All @@ -79,7 +112,7 @@ jobs:
uses: actions/checkout@v4

- name: Cache dependencies
uses: actions/cache@v1
uses: actions/cache@v4
with:
path: ~/.composer/cache/files
key: dependencies-composer-${{ hashFiles('composer.json') }}
Expand All @@ -92,7 +125,7 @@ jobs:

- name: Install dependencies
run: |
composer install --no-interaction --no-suggest --dev
composer install --no-interaction
composer dump

- name: Execute Pint
Expand All @@ -108,7 +141,7 @@ jobs:
uses: actions/checkout@v4

- name: Cache dependencies
uses: actions/cache@v1
uses: actions/cache@v4
with:
path: ~/.composer/cache/files
key: dependencies-composer-${{ hashFiles('composer.json') }}
Expand All @@ -121,7 +154,7 @@ jobs:

- name: Install dependencies
run: |
composer install --no-interaction --no-suggest --dev
composer install --no-interaction
composer dump

- name: Execute Larastan
Expand Down
13 changes: 11 additions & 2 deletions src/Commands/GenerateEnumsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,26 @@ public function handle(): int
{
$builder = $this->builder();

$this->generateEnums($builder);
$this->generateAbstractEnum($builder);

return self::SUCCESS;
}

protected function generateEnums(EnumBuilder $builder): void
{
$generatedEnums = $this->enums()
->map(fn (ReflectionEnum $enum) => app(EnumGenerator::class, ['enum' => $enum, 'builder' => $builder])())
->filter();

$this->components->info("{$generatedEnums->count()} enums have been (re)generated.");
}

protected function generateAbstractEnum(EnumBuilder $builder): void
{
app(AbstractEnumGenerator::class, ['builder' => $builder])();

$this->components->info('Abstract enum class has been (re)generated.');

return self::SUCCESS;
}

/**
Expand Down
123 changes: 117 additions & 6 deletions src/Commands/MakeEnumMethodCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,25 @@
use Exception;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Support\Str;
use InvalidArgumentException;
use Kirschbaum\Paragon\Concerns\Builders\EnumBuilder;
use Kirschbaum\Paragon\Concerns\Builders\EnumJsBuilder;
use Kirschbaum\Paragon\Concerns\Builders\EnumTsBuilder;
use Kirschbaum\Paragon\Concerns\DiscoverEnums;
use Kirschbaum\Paragon\Generators\AbstractEnumGenerator;
use Kirschbaum\Paragon\Generators\EnumGenerator;
use ReflectionEnum;
use ReflectionException;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;
use UnitEnum;

use function Laravel\Prompts\search;
use function Laravel\Prompts\text;

#[AsCommand(name: 'paragon:enum:add-method', description: 'Create a new global typescript method to be applied to every generated enum')]
Expand All @@ -28,9 +39,9 @@ public function handle(): ?bool
{
parent::handle();

app(AbstractEnumGenerator::class, ['builder' => $this->builder()])();
$this->runGenerator();

$this->components->info("Abstract enum class has been rebuilt to include new [{$this->name()}] method.");
$this->writeInfo();

return true;
}
Expand Down Expand Up @@ -70,6 +81,38 @@ protected function promptForMissingArgumentsUsing(): array
];
}

/**
* Interact further with the user if they were prompted for missing arguments.
*
* @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter
*/
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output): void
{
if (
is_string($this->option('enum'))
|| $this->option('global')
) {
return;
}

/**
* @var string $enumPath
*/
$enumPath = config('paragon.enums.paths.php');

$enums = DiscoverEnums::within(app_path($enumPath))
->mapWithKeys(fn (ReflectionEnum $reflector) => [$reflector->getName() => $reflector->getName()]);

$enum = search(
label: 'Which enum should this method be created for?',
options: fn (string $value) => strlen($value) > 0
? $enums->filter(fn (string $enum): bool => Str::contains($enum, $value, ignoreCase: true))->all()
: []
);

$input->setOption('enum', $enum);
}

/**
* Build the file with the given name.
*
Expand All @@ -88,15 +131,30 @@ protected function buildClass($name): string
/**
* Get the destination class path.
*
* @throws Exception
* @throws Throwable
*/
protected function getPath($name): string
{
/** @var string */
$methods = config('paragon.enums.paths.methods');
/**
* @var string $path
*/
$path = config('paragon.enums.paths.methods');
$extension = $this->option('javascript') ? 'js' : 'ts';

return resource_path($methods) . "/{$this->name()}.{$extension}";
if (! $this->option('global')) {
$enum = str($this->enumOption())->replace('/', '\\');

throw_unless(
enum_exists($enum->toString()),
ReflectionException::class,
"Class \"{$enum}\" does not exist"
);

$path = $enum->replace('\\', '/')
->prepend(Str::finish($path, '/'));
}

return resource_path($path) . "/{$this->name()}.{$extension}";
}

/**
Expand All @@ -120,7 +178,48 @@ protected function builder(): EnumBuilder
return $this->option('javascript')
? app(EnumJsBuilder::class)
: app(EnumTsBuilder::class);
}

/**
* @throws ReflectionException
* @throws Throwable
*/
protected function runGenerator(): void
{
$this->option('global')
? app(AbstractEnumGenerator::class, ['builder' => $this->builder()])()
: app(EnumGenerator::class, [
'enum' => new ReflectionEnum($this->enumOption()),
'builder' => $this->builder(),
'forceRegenerate' => true,
])();
}

protected function writeInfo(): void
{
$name = $this->option('global')
? 'Abstract'
: Str::afterLast($this->enumOption(), '\\');

$this->components->info("[{$name}] enum class has been rebuilt to include new [{$this->name()}()] method.");
}

/**
* @return class-string<UnitEnum>
*
* @throws Throwable
*/
protected function enumOption(): string
{
$enum = $this->option('enum');

throw_unless(
is_string($enum) && is_a($enum, UnitEnum::class, true),
InvalidArgumentException::class,
'The enum option must be a valid class-string of a UnitEnum'
);

return $enum;
}

/**
Expand All @@ -131,6 +230,18 @@ protected function builder(): EnumBuilder
protected function getOptions(): array
{
return [
new InputOption(
name: 'enum',
shortcut: 'e',
mode: InputOption::VALUE_REQUIRED,
description: 'Fully qualified namespace of enum to use',
),
new InputOption(
name: 'global',
shortcut: 'g',
mode: InputOption::VALUE_NONE,
description: 'Create global enum method',
),
new InputOption(
name: 'javascript',
shortcut: 'j',
Expand Down
2 changes: 1 addition & 1 deletion src/Concerns/DiscoverEnums.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use Illuminate\Support\Collection;
use ReflectionEnum;
use ReflectionException;
use SplFileInfo;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use UnitEnum;

class DiscoverEnums
Expand Down
Loading