Skip to content

Commit 775b6d1

Browse files
committed
Methods for individual enums
1 parent 5a423a7 commit 775b6d1

File tree

9 files changed

+368
-45
lines changed

9 files changed

+368
-45
lines changed

src/Commands/GenerateEnumsCommand.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,26 @@ public function handle(): int
2828
{
2929
$builder = $this->builder();
3030

31+
$this->generateEnums($builder);
32+
$this->generateAbstractEnum($builder);
33+
34+
return self::SUCCESS;
35+
}
36+
37+
protected function generateEnums(EnumBuilder $builder): void
38+
{
3139
$generatedEnums = $this->enums()
3240
->map(fn (ReflectionEnum $enum) => app(EnumGenerator::class, ['enum' => $enum, 'builder' => $builder])())
3341
->filter();
3442

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

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

3950
$this->components->info('Abstract enum class has been (re)generated.');
40-
41-
return self::SUCCESS;
4251
}
4352

4453
/**

src/Commands/MakeEnumMethodCommand.php

Lines changed: 117 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,25 @@
55
use Exception;
66
use Illuminate\Console\GeneratorCommand;
77
use Illuminate\Contracts\Filesystem\FileNotFoundException;
8+
use Illuminate\Support\Str;
9+
use InvalidArgumentException;
810
use Kirschbaum\Paragon\Concerns\Builders\EnumBuilder;
911
use Kirschbaum\Paragon\Concerns\Builders\EnumJsBuilder;
1012
use Kirschbaum\Paragon\Concerns\Builders\EnumTsBuilder;
13+
use Kirschbaum\Paragon\Concerns\DiscoverEnums;
1114
use Kirschbaum\Paragon\Generators\AbstractEnumGenerator;
15+
use Kirschbaum\Paragon\Generators\EnumGenerator;
16+
use ReflectionEnum;
17+
use ReflectionException;
1218
use Symfony\Component\Console\Attribute\AsCommand;
1319
use Symfony\Component\Console\Input\InputArgument;
20+
use Symfony\Component\Console\Input\InputInterface;
1421
use Symfony\Component\Console\Input\InputOption;
22+
use Symfony\Component\Console\Output\OutputInterface;
23+
use Throwable;
24+
use UnitEnum;
1525

26+
use function Laravel\Prompts\search;
1627
use function Laravel\Prompts\text;
1728

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

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

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

3546
return true;
3647
}
@@ -70,6 +81,38 @@ protected function promptForMissingArgumentsUsing(): array
7081
];
7182
}
7283

84+
/**
85+
* Interact further with the user if they were prompted for missing arguments.
86+
*
87+
* @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter
88+
*/
89+
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output): void
90+
{
91+
if (
92+
is_string($this->option('enum'))
93+
|| $this->option('global')
94+
) {
95+
return;
96+
}
97+
98+
/**
99+
* @var string $enumPath
100+
*/
101+
$enumPath = config('paragon.enums.paths.php');
102+
103+
$enums = DiscoverEnums::within(app_path($enumPath))
104+
->mapWithKeys(fn (ReflectionEnum $reflector) => [$reflector->getName() => $reflector->getName()]);
105+
106+
$enum = search(
107+
label: 'Which enum should this method be created for?',
108+
options: fn (string $value) => strlen($value) > 0
109+
? $enums->filter(fn (string $enum): bool => Str::contains($enum, $value, ignoreCase: true))->all()
110+
: []
111+
);
112+
113+
$input->setOption('enum', $enum);
114+
}
115+
73116
/**
74117
* Build the file with the given name.
75118
*
@@ -88,15 +131,30 @@ protected function buildClass($name): string
88131
/**
89132
* Get the destination class path.
90133
*
91-
* @throws Exception
134+
* @throws Throwable
92135
*/
93136
protected function getPath($name): string
94137
{
95-
/** @var string */
96-
$methods = config('paragon.enums.paths.methods');
138+
/**
139+
* @var string $path
140+
*/
141+
$path = config('paragon.enums.paths.methods');
97142
$extension = $this->option('javascript') ? 'js' : 'ts';
98143

99-
return resource_path($methods) . "/{$this->name()}.{$extension}";
144+
if (! $this->option('global')) {
145+
$enum = str($this->enumOption())->replace('/', '\\');
146+
147+
throw_unless(
148+
enum_exists($enum->toString()),
149+
ReflectionException::class,
150+
"Class \"{$enum}\" does not exist"
151+
);
152+
153+
$path = $enum->replace('\\', '/')
154+
->prepend(Str::finish($path, '/'));
155+
}
156+
157+
return resource_path($path) . "/{$this->name()}.{$extension}";
100158
}
101159

102160
/**
@@ -120,7 +178,48 @@ protected function builder(): EnumBuilder
120178
return $this->option('javascript')
121179
? app(EnumJsBuilder::class)
122180
: app(EnumTsBuilder::class);
181+
}
182+
183+
/**
184+
* @throws ReflectionException
185+
* @throws Throwable
186+
*/
187+
protected function runGenerator(): void
188+
{
189+
$this->option('global')
190+
? app(AbstractEnumGenerator::class, ['builder' => $this->builder()])()
191+
: app(EnumGenerator::class, [
192+
'enum' => new ReflectionEnum($this->enumOption()),
193+
'builder' => $this->builder(),
194+
'forceRegenerate' => true,
195+
])();
196+
}
197+
198+
protected function writeInfo(): void
199+
{
200+
$name = $this->option('global')
201+
? 'Abstract'
202+
: Str::afterLast($this->enumOption(), '\\');
123203

204+
$this->components->info("[{$name}] enum class has been rebuilt to include new [{$this->name()}()] method.");
205+
}
206+
207+
/**
208+
* @return class-string<UnitEnum>
209+
*
210+
* @throws Throwable
211+
*/
212+
protected function enumOption(): string
213+
{
214+
$enum = $this->option('enum');
215+
216+
throw_unless(
217+
is_string($enum) && is_a($enum, UnitEnum::class, true),
218+
InvalidArgumentException::class,
219+
'The enum option must be a valid class-string of a UnitEnum'
220+
);
221+
222+
return $enum;
124223
}
125224

126225
/**
@@ -131,6 +230,18 @@ protected function builder(): EnumBuilder
131230
protected function getOptions(): array
132231
{
133232
return [
233+
new InputOption(
234+
name: 'enum',
235+
shortcut: 'e',
236+
mode: InputOption::VALUE_REQUIRED,
237+
description: 'Fully qualified namespace of enum to use',
238+
),
239+
new InputOption(
240+
name: 'global',
241+
shortcut: 'g',
242+
mode: InputOption::VALUE_NONE,
243+
description: 'Create global enum method',
244+
),
134245
new InputOption(
135246
name: 'javascript',
136247
shortcut: 'j',

src/Concerns/DiscoverEnums.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
use Illuminate\Support\Collection;
66
use ReflectionEnum;
77
use ReflectionException;
8-
use SplFileInfo;
98
use Symfony\Component\Finder\Finder;
9+
use Symfony\Component\Finder\SplFileInfo;
1010
use UnitEnum;
1111

1212
class DiscoverEnums

src/Generators/AbstractEnumGenerator.php

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@
66
use Illuminate\Support\Collection;
77
use Illuminate\Support\Facades\Storage;
88
use Kirschbaum\Paragon\Concerns\Builders\EnumBuilder;
9-
use SplFileInfo;
109
use Symfony\Component\Filesystem\Filesystem as FileUtility;
1110
use Symfony\Component\Finder\Exception\DirectoryNotFoundException;
1211
use Symfony\Component\Finder\Finder;
12+
use Symfony\Component\Finder\SplFileInfo;
1313

1414
class AbstractEnumGenerator
1515
{
1616
protected Filesystem $files;
1717

1818
public function __construct(protected EnumBuilder $builder)
1919
{
20-
/** @var string */
20+
/**
21+
* @var string $generatedPath
22+
*/
2123
$generatedPath = config('paragon.enums.paths.generated');
2224

2325
$this->files = Storage::createLocalDriver([
@@ -37,7 +39,9 @@ protected function contents(): string
3739
{
3840
$imports = $this->imports();
3941
$suffix = $imports->count() ? PHP_EOL : '';
40-
/** @var string */
42+
/**
43+
* @var string $abstractClass
44+
*/
4145
$abstractClass = config('paragon.enums.abstract-class');
4246

4347
return str((string) file_get_contents($this->builder->abstractStubPath()))
@@ -53,12 +57,15 @@ protected function contents(): string
5357
*/
5458
protected function imports(): Collection
5559
{
56-
/** @var string */
60+
/**
61+
* @var string $methodsPath
62+
*/
5763
$methodsPath = config('paragon.enums.paths.methods');
5864

5965
try {
6066
$files = Finder::create()
6167
->files()
68+
->depth(0)
6269
->in(resource_path($methodsPath));
6370
} catch (DirectoryNotFoundException) {
6471
return collect();
@@ -72,17 +79,21 @@ protected function imports(): Collection
7279
return $fileCollection
7380
->mapWithKeys(function (SplFileInfo $file): array {
7481
$filesystem = new FileUtility();
75-
/** @var string */
82+
/**
83+
* @var string $generatedPath
84+
*/
7685
$generatedPath = config('paragon.enums.paths.generated');
7786

7887
$relativeFilePath = $filesystem->makePathRelative(
7988
$file->getPath(),
8089
resource_path($generatedPath)
8190
);
8291

83-
$name = (string) str($file->getFileName())->before('.');
92+
$name = $file->getBasename($this->builder->fileExtension());
8493

85-
/** @var array<string,string> */
94+
/**
95+
* @var array<string,string>
96+
*/
8697
return [$name => "import {$name} from '{$relativeFilePath}{$file->getFilename()}';" . PHP_EOL];
8798
})
8899
->sort();

0 commit comments

Comments
 (0)