Skip to content

Commit

Permalink
Polishing
Browse files Browse the repository at this point in the history
  • Loading branch information
roxblnfk committed Jul 18, 2024
1 parent 3abc193 commit ea3fdc5
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 99 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
/vendor/
/.env
*.log
*.exe
Binary file not shown.
107 changes: 8 additions & 99 deletions src/Command/Get.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Internal\DLoad\Command;

use Internal\DLoad\Bootstrap;
use Internal\DLoad\DLoad;
use Internal\DLoad\Module\Archive\ArchiveFactory;
use Internal\DLoad\Module\Common\Architecture;
use Internal\DLoad\Module\Common\Config\Destination;
Expand Down Expand Up @@ -90,113 +91,21 @@ protected function execute(
environment: \getenv(),
)->finish();
$container->set($input, InputInterface::class);
$container->set($output, OutputInterface::class);
$container->set(new SymfonyStyle($input, $output), StyleInterface::class);
$container->set($this->logger);

$output->writeln('Architecture: ' . $container->get(Architecture::class)->name);
$output->writeln(' Op. system: ' . $container->get(OperatingSystem::class)->name);
$output->writeln(' Stability: ' . $container->get(Stability::class)->name);

/** @var SoftwareCollection $softwareCollection */
$softwareCollection = $container->get(SoftwareCollection::class);
/** @var ArchiveFactory $archiveFactory */
$archiveFactory = $container->get(ArchiveFactory::class);
/** @var Downloader $downloader */
$downloader = $container->get(Downloader::class);

// / /*
$task = $downloader->download(
$softwareCollection->findSoftware('rr') ?? throw new \RuntimeException('Software not found.'),
static fn() => null,
);
/*/
$task = new \Internal\DLoad\Module\Downloader\Task\DownloadTask(
$softwareCollection->findSoftware('rr') ?? throw new \RuntimeException('Software not found.'),
static fn() => null,
fn(): \React\Promise\PromiseInterface => \React\Promise\resolve(new DownloadResult(
new \SplFileInfo('C:\Users\test\AppData\Local\Temp\roadrunner-2024.1.5-windows-amd64.zip'),
'2024.1.5'
)),
);
//*/

($task->handler)()->then(
function (DownloadResult $downloadResult) use ($task, $archiveFactory, $output): void {
$fileInfo = $downloadResult->file;
$archive = $archiveFactory->create($fileInfo);
$extractor = $archive->extract();

while ($extractor->valid()) {
$file = $extractor->current();
\assert($file instanceof \SplFileInfo);

$to = $this->shouldBeExtracted($file, $task->software->files);

if ($to === null || !$this->checkExisting($to)) {
$extractor->next();
continue;
}

$extractor->send($to);

// Success
$path = $to->getRealPath() ?: $to->getPathname();
$output->writeln(\sprintf(
'%s (<comment>%s</comment>) has been installed into <info>%s</info>',
$to->getFilename(),
$downloadResult->version,
$path,
));

$to->isExecutable() or @\chmod($path, 0755);
}
},
);
/** @var DLoad $dload */
$dload = $container->get(DLoad::class);

return Command::SUCCESS;
}

/**
* @return bool True if the file should be extracted, false otherwise.
*/
private function checkExisting(\SplFileInfo $bin): bool
{
if (! \is_file($bin->getPathname())) {
return true;
}

/** @var StyleInterface $io */
$io = $this->container->get(StyleInterface::class);
$io->warning('File already exists: ' . $bin->getPathname());
if (!$io->confirm('Do you want overwrite it?', false)) {
$io->note('Skipping ' . $bin->getFilename() . ' installation...');
return false;
}
$binary = $input->getArgument('binary');
$dload->addTask($binary);
$dload->run();

return true;
}

/**
* @param array<File> $mapping
*/
private function shouldBeExtracted(\SplFileInfo $source, array $mapping): ?\SplFileInfo
{
/** @var Destination $destination */
$destination = $this->container->get(Destination::class);
$path = $destination->path ?? \getcwd();

foreach ($mapping as $conf) {
if (\preg_match($conf->pattern, $source->getFilename())) {
$newName = match(true) {
$conf->rename === null => $source->getFilename(),
$source->getExtension() === '' => $conf->rename,
default => $conf->rename . '.' . $source->getExtension(),
};

return new \SplFileInfo($path . DIRECTORY_SEPARATOR . $newName);
}
}

return null;
return Command::SUCCESS;
}
}
154 changes: 154 additions & 0 deletions src/DLoad.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<?php

declare(strict_types=1);

namespace Internal\DLoad;

use Internal\DLoad\Module\Archive\ArchiveFactory;
use Internal\DLoad\Module\Common\Config\Destination;
use Internal\DLoad\Module\Common\Config\Embed\File;
use Internal\DLoad\Module\Common\Config\Embed\Software;
use Internal\DLoad\Module\Downloader\Downloader;
use Internal\DLoad\Module\Downloader\SoftwareCollection;
use Internal\DLoad\Module\Downloader\Task\DownloadResult;
use Internal\DLoad\Module\Downloader\Task\DownloadTask;
use Internal\DLoad\Module\Downloader\TaskManager;
use React\Promise\PromiseInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\StyleInterface;

use function React\Promise\resolve;

/**
* To have a short syntax.
*
* @internal
*/
final class DLoad
{
public bool $useMock = false;

public function __construct(
private readonly TaskManager $taskManager,
private readonly SoftwareCollection $softwareCollection,
private readonly Downloader $downloader,
private readonly ArchiveFactory $archiveFactory,
private readonly Destination $configDestination,
private readonly OutputInterface $output,
private readonly StyleInterface $io,
) {}

public function addTask(
string $softwareName,
): void {
$this->useMock and $softwareName = 'rr';
$this->taskManager->addTask(function () use ($softwareName): void {
// Find Software
$software = $this->softwareCollection->findSoftware($softwareName) ?? throw new \RuntimeException(
'Software not found.',
);

// Create a Download task
$task = $this->prepareDownloadTask($software);

// Extract files
($task->handler)()->then($this->prepareExtractTask($software));
});
}

public function run(): void
{
$this->taskManager->await();
}

private function prepareDownloadTask(Software $software): DownloadTask
{
return $this->useMock
? new DownloadTask(
$software,
static fn() => null,
static fn(): PromiseInterface => resolve(
new DownloadResult(
new \SplFileInfo(Info::ROOT_DIR . '/resources/mock/roadrunner-2024.1.5-windows-amd64.zip'),
'2024.1.5',
),
),
)
: $this->downloader->download($software, static fn() => null);
}

/**
* @return \Closure(DownloadResult): void
*/
private function prepareExtractTask(Software $software): \Closure
{
return function (DownloadResult $downloadResult) use ($software): void {
$fileInfo = $downloadResult->file;
$archive = $this->archiveFactory->create($fileInfo);
$extractor = $archive->extract();

while ($extractor->valid()) {
$file = $extractor->current();
\assert($file instanceof \SplFileInfo);

$to = $this->shouldBeExtracted($file, $software->files);

if ($to === null || !$this->checkExisting($to)) {
$extractor->next();
continue;
}

$extractor->send($to);

// Success
$path = $to->getRealPath() ?: $to->getPathname();
$this->output->writeln(\sprintf(
'%s (<comment>%s</comment>) has been installed into <info>%s</info>',
$to->getFilename(),
$downloadResult->version,
$path,
));

$to->isExecutable() or @\chmod($path, 0755);
}
};
}

/**
* @return bool True if the file should be extracted, false otherwise.
*/
private function checkExisting(\SplFileInfo $bin): bool
{
if (\is_file($bin->getPathname())) {
$this->io->warning('File already exists: ' . $bin->getPathname());
if (!$this->io->confirm('Do you want overwrite it?', false)) {
$this->io->note('Skipping ' . $bin->getFilename() . ' installation...');
return false;
}
}

return true;
}

/**
* @param list<File> $mapping
*/
private function shouldBeExtracted(\SplFileInfo $source, array $mapping): ?\SplFileInfo
{
$path = $this->configDestination->path ?? \getcwd();

foreach ($mapping as $conf) {
if (\preg_match($conf->pattern, $source->getFilename())) {
$newName = match(true) {
$conf->rename === null => $source->getFilename(),
$source->getExtension() === '' => $conf->rename,
default => $conf->rename . '.' . $source->getExtension(),
};

return new \SplFileInfo($path . DIRECTORY_SEPARATOR . $newName);
}
}

return null;
}
}
1 change: 1 addition & 0 deletions src/Module/Downloader/TaskManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public function getProcessor(): \Generator
try {
if ($task->isTerminated()) {
unset($this->tasks[$key]);
continue;
}

if (!$task->isStarted()) {
Expand Down

0 comments on commit ea3fdc5

Please sign in to comment.