Skip to content

Commit

Permalink
Merge pull request #11 from TheDragonCode/1.x
Browse files Browse the repository at this point in the history
Improved generation of helper files for IDE
  • Loading branch information
andrey-helldar authored Aug 24, 2024
2 parents 559a6a5 + 630219e commit c5c4c31
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 77 deletions.
141 changes: 100 additions & 41 deletions src/Commands/GenerateHelperCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,28 @@

namespace DragonCode\LaravelHttpMacros\Commands;

use Closure;
use DragonCode\LaravelHttpMacros\Macros\Macro;
use DragonCode\Support\Facades\Filesystem\Directory;
use DragonCode\Support\Facades\Filesystem\File;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use ReflectionFunction;
use ReflectionNamedType;
use ReflectionParameter;
use ReflectionUnionType;

use function array_map;
use function base_path;
use function class_exists;
use function collect;
use function config;
use function file_get_contents;
use function implode;
use function is_string;
use function is_numeric;
use function Laravel\Prompts\intro;
use function Laravel\Prompts\outro;
use function sprintf;

use const PHP_EOL;
use function var_export;

class GenerateHelperCommand extends Command
{
Expand All @@ -29,71 +35,124 @@ class GenerateHelperCommand extends Command

public function handle(): void
{
$names = $this->names();
$this->sections()->map(function (array $macros, string $section) {
intro($section);

return $this->macros($macros);
})
->tap(fn () => outro('storing'))
->tap(fn () => $this->cleanUp())
->each(fn (Collection $blocks, string $section) => $this->store(
$section,
$this->compileBlocks($section, $blocks->flatten())
));
}

protected function cleanUp(): void
{
$this->components->task('clean up', fn () => Directory::ensureDelete($this->directory()));
}

protected function macros(array $macros): Collection
{
return collect($macros)->map(function (Macro|string $macro, int|string $name) {
$name = $this->resolveName($macro, $name);

$static = $this->make($names, true);
$dynamic = $this->make($names);
$this->components->task($name, function () use ($macro, $name, &$result) {
$result = $this->prepare($name, $this->reflectionCallback($macro::callback())->getParameters());
});

$this->cleanUp();
$this->store($static, true);
$this->store($dynamic);
return $result;
});
}

protected function make(array $names, bool $isStatic = false): array
protected function store(string $section, string $content): void
{
return array_map(
fn (string $name) => sprintf(
' * @method %s $this %s(\Closure|string $class, int|string|null $key = null)',
$isStatic ? 'static' : '',
$name
),
$names
);
$this->components->task($section, fn () => File::store($this->helperPath($section), $content));
}

protected function store(array $methods, bool $isStatic = false): void
protected function compileBlocks(string $section, Collection $blocks): string
{
File::store(
$this->path($this->filename($isStatic)),
$this->makeDocBlock($methods)
return Str::replace(
['{class}', '{methods}'],
[
Str::studly($section),
$blocks->implode("\n"),
],
$this->stub()
);
}

protected function makeDocBlock(array $methods): string
protected function prepare(string $name, array $functions): array
{
return Str::replace('{methods}', implode(PHP_EOL, $methods), $this->template());
return $this->docBlock($name, $this->docBlockParameters($functions));
}

protected function names(): array
protected function docBlock(string $name, string $parameters): array
{
return collect($this->macros())->map(
fn (Macro|string $macro, int|string $name) => is_string($name) ? $name : $macro::name()
)->all();
return [
sprintf(' * @method $this %s(%s)', $name, $parameters),
sprintf(' * @method static $this %s(%s)', $name, $parameters),
];
}

protected function path(?string $filename = null): string
/**
* @param array<ReflectionParameter> $functions
*
* @return Collection
*/
protected function docBlockParameters(array $functions): string
{
return base_path('vendor/_http_macros/' . $filename);
return collect($functions)->map(function (ReflectionParameter $parameter) {
$result = $parameter->hasType() ? $this->compactTypes($parameter->getType()) : 'mixed';

$result .= ' $' . $parameter->getName();

if ($parameter->isDefaultValueAvailable()) {
$result .= ' = ' . var_export($parameter->getDefaultValue(), true);
}

return $result;
})->implode(', ');
}

protected function filename(bool $isStatic): string
protected function compactTypes(ReflectionNamedType|ReflectionUnionType $type): string
{
return $isStatic
? '_ide_helper_macro_static.php'
: '_ide_helper_macro.php';
if ($type instanceof ReflectionNamedType) {
return class_exists($type->getName()) ? '\\' . $type->getName() : $type->getName();
}

return collect($type->getTypes())->map(
fn (ReflectionNamedType $type) => $this->compactTypes($type)
)->implode('|');
}

protected function cleanUp(): void
protected function reflectionCallback(Closure $callback): ReflectionFunction
{
return new ReflectionFunction($callback);
}

protected function resolveName(Macro|string $macro, int|string $name): string
{
return is_numeric($name) ? $macro::name() : $name;
}

protected function sections(): Collection
{
return collect(config('http.macros', []));
}

protected function helperPath(string $name): string
{
Directory::ensureDelete($this->path());
return $this->directory() . "/_ide_helper_macro_$name.php";
}

protected function macros(): array
protected function directory(): string
{
return config('http.macros.response', []);
return base_path('vendor/_http_macros');
}

protected function template(): string
protected function stub(): string
{
return file_get_contents(__DIR__ . '/../../stubs/helper.stub');
}
Expand Down
2 changes: 1 addition & 1 deletion stubs/helper.stub
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ namespace Illuminate\Http\Client {
/**
{methods}
*/
class Response {}
class {class} {}
}
5 changes: 1 addition & 4 deletions tests/Datasets/types.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,4 @@

declare(strict_types=1);

dataset('types', [
['static', '_static'],
['dynamic', ''],
]);
dataset('types', ['request', 'response']);
2 changes: 1 addition & 1 deletion tests/Helpers/content.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

function content(string $filename): string
{
return trim(file_get_contents($filename));
return file_get_contents($filename);
}
12 changes: 0 additions & 12 deletions tests/Snapshots/dynamic

This file was deleted.

11 changes: 11 additions & 0 deletions tests/Snapshots/request
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

/** @noinspection all */

namespace Illuminate\Http\Client {
/**
* @method $this withLogger(string $channel)
* @method static $this withLogger(string $channel)
*/
class Request {}
}
15 changes: 15 additions & 0 deletions tests/Snapshots/response
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

/** @noinspection all */

namespace Illuminate\Http\Client {
/**
* @method $this toData(\Closure|string $class, string|int|null $key = NULL)
* @method static $this toData(\Closure|string $class, string|int|null $key = NULL)
* @method $this toDataCollection(\Closure|string $class, string|int|null $key = NULL)
* @method static $this toDataCollection(\Closure|string $class, string|int|null $key = NULL)
* @method $this toFoo(\Closure|string $class, string|int|null $key = NULL)
* @method static $this toFoo(\Closure|string $class, string|int|null $key = NULL)
*/
class Response {}
}
12 changes: 0 additions & 12 deletions tests/Snapshots/static

This file was deleted.

12 changes: 6 additions & 6 deletions tests/Unit/Commands/GenerateHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
fn () => Directory::ensureDelete(base_path('vendor/_http_macros'))
);

test('new generation', closure: function (string $type, string $name) {
test('new generation', function (string $type) {
$directory = base_path('vendor/_http_macros');
$filename = $directory . "/_ide_helper_macro$name.php";
$filename = $directory . "/_ide_helper_macro_$type.php";
$snapshot = __DIR__ . "/../../Snapshots/$type";

expect($directory)->not->toBeDirectory();
Expand All @@ -25,9 +25,9 @@
expect(content($filename))->toBe(content($snapshot));
})->with('types');

test('replace', closure: function (string $type, string $name) {
test('replace', function (string $type) {
$directory = base_path('vendor/_http_macros');
$filename = $directory . "/_ide_helper_macro$name.php";
$filename = $directory . "/_ide_helper_macro_$type.php";
$snapshot = __DIR__ . "/../../Snapshots/$type";

expect($directory)->not->toBeDirectory();
Expand All @@ -40,9 +40,9 @@
expect(content($filename))->toBe(content($snapshot));
})->with('types');

test('clean up', closure: function (string $type, string $name) {
test('clean up', function (string $type) {
$directory = base_path('vendor/_http_macros');
$filename = $directory . "/_ide_helper_macro$name.php";
$filename = $directory . "/_ide_helper_macro_$type.php";
$snapshot = __DIR__ . "/../../Snapshots/$type";

expect($directory)->not->toBeDirectory();
Expand Down

0 comments on commit c5c4c31

Please sign in to comment.