Skip to content

Commit

Permalink
Options --from & --to can be set from the config (Close #112) (#113)
Browse files Browse the repository at this point in the history
* Options --from & --to can be set from the config (Close #112)

* Increase coverage

* cs
  • Loading branch information
kubk authored Aug 27, 2023
1 parent d8b5dfd commit 373e938
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 29 deletions.
3 changes: 0 additions & 3 deletions bin/default-config.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
declare(strict_types=1);

use Riverwaysoft\PhpConverter\Ast\DtoVisitor;
use Riverwaysoft\PhpConverter\CodeProvider\FileSystemCodeProvider;
use Riverwaysoft\PhpConverter\Filter\Attributes\Dto;
use Riverwaysoft\PhpConverter\Config\PhpConverterConfig;
use Riverwaysoft\PhpConverter\Filter\PhpAttributeFilter;
Expand All @@ -14,8 +13,6 @@
use Riverwaysoft\PhpConverter\OutputWriter\SingleFileOutputWriter\SingleFileOutputWriter;

return static function (PhpConverterConfig $config) {
$config->setCodeProvider(new FileSystemCodeProvider('/\.php$/'));

$config->addVisitor(new DtoVisitor(new PhpAttributeFilter(Dto::class)));

$config->setOutputGenerator(new TypeScriptOutputGenerator(
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"doctrine/inflector": "^2.0",
"composer/xdebug-handler": "^3.0",
"symfony/console": "^5.3|^6.3|^6.2|^6.1",
"symfony/filesystem": "^5.3|^6.3|^6.2|^6.1"
"symfony/filesystem": "^5.3|^6.3|^6.2|^6.1",
"symfony/process": "^5.3|^6.3|^6.2|^6.1"
},
"license": "GPL-3.0-or-later",
"autoload": {
Expand Down
18 changes: 8 additions & 10 deletions src/Cli/ConvertCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Riverwaysoft\PhpConverter\Ast\UsageCollector;
use Riverwaysoft\PhpConverter\Ast\Converter;
use Riverwaysoft\PhpConverter\Config\PhpConverterConfig;
use Riverwaysoft\PhpConverter\OutputDiffRenderer\OutputDiffRenderer;
use Riverwaysoft\PhpConverter\DiffRenderer\DiffRenderer;
use Composer\XdebugHandler\XdebugHandler;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
Expand All @@ -27,15 +27,15 @@ class ConvertCommand extends Command

private UsageCollector $usageCollector;

private OutputDiffRenderer $diffWriter;
private DiffRenderer $diffRenderer;

private Filesystem $fileSystem;

public function __construct()
{
parent::__construct();
$this->usageCollector = new UsageCollector();
$this->diffWriter = new OutputDiffRenderer();
$this->diffRenderer = new DiffRenderer();
$this->fileSystem = new Filesystem();
}

Expand All @@ -45,6 +45,7 @@ protected function configure(): void
->setDescription('Generate TypeScript / Dart from PHP sources')
->addOption('from', 'f', InputOption::VALUE_REQUIRED)
->addOption('to', 't', InputOption::VALUE_REQUIRED)
->addOption('branch', 'b', InputOption::VALUE_OPTIONAL)
->addOption(
name: 'config',
shortcut: 'c',
Expand All @@ -62,18 +63,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$this->turnOffXdebug();
}

$from = $input->getOption('from');
$to = $input->getOption('to');
Assert::directory($to);

$configFile = $input->getOption('config');
Assert::file($configFile);
Assert::readable($configFile);

$config = new PhpConverterConfig();
$config = new PhpConverterConfig($input);
(require_once $configFile)($config);

$files = $config->getCodeProvider()->getListings($from);
$files = $config->getCodeProvider()->getListings();
if (empty($files)) {
$output->writeln('No files to convert');
return Command::SUCCESS;
Expand All @@ -88,10 +85,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$outputFiles = $config->getOutputGenerator()->generate($converterResult);

foreach ($outputFiles as $outputFile) {
$to = $config->getToDirectory();
$outputAbsolutePath = sprintf("%s/%s", rtrim($to, '/'), $outputFile->getRelativeName());
$newFileContent = $outputFile->getContent();
if ($this->fileSystem->exists($outputAbsolutePath)) {
$diff = $this->diffWriter->calculate(file_get_contents($outputAbsolutePath), $newFileContent);
$diff = $this->diffRenderer->calculate(file_get_contents($outputAbsolutePath), $newFileContent);
if (empty($diff)) {
$output->writeln(sprintf("\nNo difference between the old generated file and the new one: %s", $outputFile->getRelativeName()));
} else {
Expand Down
11 changes: 11 additions & 0 deletions src/CodeProvider/CodeProviderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Riverwaysoft\PhpConverter\CodeProvider;

interface CodeProviderInterface
{
/** @return string[] */
public function getListings(): iterable;
}
12 changes: 9 additions & 3 deletions src/CodeProvider/FileSystemCodeProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,23 @@
use RegexIterator;
use function file_get_contents;

class FileSystemCodeProvider
class FileSystemCodeProvider implements CodeProviderInterface
{
public function __construct(
private string $pattern,
private string $directory,
) {
}

public static function phpFiles(string $directory): self
{
return new self('/\.php$/', $directory);
}

/** @return string[] */
public function getListings(string $directory): iterable
public function getListings(): iterable
{
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->directory));
$files = new RegexIterator($files, $this->pattern);

foreach ($files as $file) {
Expand Down
38 changes: 38 additions & 0 deletions src/CodeProvider/RemoteRepositoryCodeProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Riverwaysoft\PhpConverter\CodeProvider;

use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Process\Process;

class RemoteRepositoryCodeProvider implements CodeProviderInterface
{
private Filesystem $filesystem;

public function __construct(
private string $repositoryUrl,
private string $branch,
) {
$this->filesystem = new Filesystem();
}

public function getListings(): iterable
{
$repoDownloadFolder = $this->generateRepoDownloadFolder($this->repositoryUrl);
if ($this->filesystem->exists($repoDownloadFolder)) {
$this->filesystem->remove($repoDownloadFolder);
}
$process = new Process(["git", "clone", "--branch", $this->branch, $this->repositoryUrl, "--depth", "1", $repoDownloadFolder]);
$process->mustRun();
$fileSystemCodeProvider = FileSystemCodeProvider::phpFiles($repoDownloadFolder);

return $fileSystemCodeProvider->getListings();
}

private function generateRepoDownloadFolder(string $repositoryUrl): string
{
return sprintf("%s/%s", sys_get_temp_dir(), basename($repositoryUrl));
}
}
60 changes: 56 additions & 4 deletions src/Config/PhpConverterConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@

namespace Riverwaysoft\PhpConverter\Config;

use Exception;
use Riverwaysoft\PhpConverter\Ast\ConverterVisitor;
use Riverwaysoft\PhpConverter\CodeProvider\CodeProviderInterface;
use Riverwaysoft\PhpConverter\CodeProvider\FileSystemCodeProvider;
use Riverwaysoft\PhpConverter\CodeProvider\RemoteRepositoryCodeProvider;
use Riverwaysoft\PhpConverter\OutputGenerator\OutputGeneratorInterface;
use Symfony\Component\Console\Input\InputInterface;
use InvalidArgumentException;
use Webmozart\Assert\Assert;

class PhpConverterConfig
{
Expand All @@ -15,7 +21,14 @@ class PhpConverterConfig

private OutputGeneratorInterface|null $outputGenerator = null;

private FileSystemCodeProvider|null $codeProvider = null;
private CodeProviderInterface|null $codeProvider = null;

private string|null $toDirectory = null;

public function __construct(
private InputInterface $input,
) {
}

public function addVisitor(ConverterVisitor $visitor): void
{
Expand All @@ -38,13 +51,52 @@ public function getOutputGenerator(): OutputGeneratorInterface
return $this->outputGenerator;
}

public function getCodeProvider(): FileSystemCodeProvider
public function setCodeProvider(CodeProviderInterface $codeProvider): void
{
$this->codeProvider = $codeProvider;
}

public function getCodeProvider(): CodeProviderInterface
{
if (!$this->codeProvider) {
$this->codeProvider = $this->guessCodeProvider();
}

return $this->codeProvider;
}

public function setCodeProvider(FileSystemCodeProvider $codeProvider): void
private function guessCodeProvider(): CodeProviderInterface
{
$this->codeProvider = $codeProvider;
$from = $this->input->getOption('from');

if (is_dir($from)) {
return FileSystemCodeProvider::phpFiles($from);
}

if ((str_starts_with(haystack: $from, needle: 'https://') || str_starts_with(haystack: $from, needle: 'git@'))
&& str_ends_with(haystack: $from, needle: '.git')) {
$branch = $this->input->getOption('branch');
if (!$branch) {
throw new InvalidArgumentException('Option --branch is required when using URL as repository source');
}

return new RemoteRepositoryCodeProvider(repositoryUrl: $from, branch: $branch);
}

throw new Exception(sprintf("Either pass --from as CLI argument or set the code provider via %s::setCodeProvider()", self::class));
}

public function getToDirectory(): string
{
$to = $this->toDirectory ?: $this->input->getOption('to');

Assert::directory($to, sprintf("Either pass --to as CLI argument or set the directory via %s::setToDirectory()", self::class));

return $to;
}

public function setToDirectory(string $toDirectory): void
{
$this->toDirectory = $toDirectory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

declare(strict_types=1);

namespace Riverwaysoft\PhpConverter\OutputDiffRenderer;
namespace Riverwaysoft\PhpConverter\DiffRenderer;

use Jfcherng\Diff\DiffHelper;
use Jfcherng\Diff\Renderer\RendererConstant;
use const JSON_UNESCAPED_SLASHES;
use const JSON_UNESCAPED_UNICODE;

class OutputDiffRenderer
class DiffRenderer
{
public function __construct(
private int $context = 3,
Expand Down
113 changes: 113 additions & 0 deletions tests/Config/PhpConverterConfigTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

declare(strict_types=1);

namespace App\Tests\Config;

use Exception;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Riverwaysoft\PhpConverter\Ast\ConverterVisitor;
use Riverwaysoft\PhpConverter\CodeProvider\CodeProviderInterface;
use Riverwaysoft\PhpConverter\CodeProvider\FileSystemCodeProvider;
use Riverwaysoft\PhpConverter\CodeProvider\RemoteRepositoryCodeProvider;
use Riverwaysoft\PhpConverter\Config\PhpConverterConfig;
use Symfony\Component\Console\Input\InputInterface;

class PhpConverterConfigTest extends TestCase
{
public function testVisitors(): void
{
$config = new PhpConverterConfig($this->createInputMock([]));

$visitor1Mock = $this->createMock(ConverterVisitor::class);
$visitor2Mock = $this->createMock(ConverterVisitor::class);

$config->addVisitor($visitor1Mock);
$config->addVisitor($visitor2Mock);

$this->assertCount(2, $config->getVisitors());
}

public function testGetCodeProviderWhenAlreadySet(): void
{
$config = new PhpConverterConfig($this->createInputMock([]));

$mockedProvider = $this->createMock(CodeProviderInterface::class);
$config->setCodeProvider($mockedProvider);

$this->assertSame($mockedProvider, $config->getCodeProvider());
}

public function testGetCodeProviderFromDirectory(): void
{
$input = $this->createInputMock([
'from' => __DIR__,
]);
$config = new PhpConverterConfig($input);

$this->assertInstanceOf(FileSystemCodeProvider::class, $config->getCodeProvider());
}

public function testGetCodeProviderFromGitLinkWithBranch(): void
{
$input = $this->createInputMock([
'from' => 'https://example.com/repo.git',
'branch' => 'main',
]);
$config = new PhpConverterConfig($input);

$this->assertInstanceOf(RemoteRepositoryCodeProvider::class, $config->getCodeProvider());
}

public function testGetCodeProviderFromGitShhLinkWithBranch(): void
{
$input = $this->createInputMock([
'from' => 'git@gitlab.com:test_soft/project/repo.git',
'branch' => 'main',
]);
$config = new PhpConverterConfig($input);

$this->assertInstanceOf(RemoteRepositoryCodeProvider::class, $config->getCodeProvider());
}

public function testGetCodeProviderFromGitLinkWithoutBranch(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Option --branch is required when using URL as repository source');

$input = $this->createInputMock([
'from' => 'https://example.com/repo.git',
'branch' => '',
]);
$config = new PhpConverterConfig($input);

$config->getCodeProvider();
}

public function testGetCodeProviderFromInvalidOption(): void
{
$this->expectException(Exception::class);

$input = $this->createInputMock([
'from' => 'invalid',
]);
$config = new PhpConverterConfig($input);

$config->getCodeProvider();
}

private function createInputMock(mixed $values): InputInterface
{
$mock = $this->createMock(InputInterface::class);
$mock->method('getOption')
->willReturnCallback(function ($argument) use ($values) {
if (isset($values[$argument])) {
return $values[$argument];
}
throw new Exception('Unknown input mock argument ' . $argument);
});

return $mock;
}
}
Loading

0 comments on commit 373e938

Please sign in to comment.