Skip to content

Commit

Permalink
Laravel Prompts (#63)
Browse files Browse the repository at this point in the history
* got rid of Presenter service

* use Laravel Prompts
  • Loading branch information
osteel authored Aug 21, 2023
1 parent b6e4644 commit 602bdce
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 214 deletions.
68 changes: 53 additions & 15 deletions app/Commands/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,65 @@

namespace App\Commands;

use App\Services\Presenter\Presenter;
use App\Services\Presenter\PresenterContract;
use Illuminate\Contracts\Container\BindingResolutionException;
use LaravelZero\Framework\Application;
use LaravelZero\Framework\Commands\Command as BaseCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

use function Laravel\Prompts\select;

abstract class Command extends BaseCommand
{
protected PresenterContract $presenter;
/** Display an error message. */
protected function failure(string $message): void
{
$this->output->block(sprintf(' 🚨 %s', $message), null, 'fg=white;bg=red', ' ', true);
}

/** Display an information message. */
protected function hint(string $message): void
{
$this->output->block(sprintf(' ℹ️ %s', $message), null, 'fg=white;bg=blue', ' ', true);
}

/** Display a success message. */
protected function success(string $message): void
{
$this->output->block(sprintf(' 🎉 %s', $message), null, 'fg=white;bg=green', ' ', true);
}

/** Display a warning message. */
protected function warning(string $message): void
{
$this->output->block(sprintf(' ⚠️ %s', $message), null, 'fg=yellow;bg=default', ' ', true);
}

protected function execute(InputInterface $input, OutputInterface $output): int
/**
* Display multiple options.
*
* @param list<string> $options
*/
protected function select(string $question, array $options, ?string $default = null): string
{
try {
$this->presenter = resolve(PresenterContract::class); // @phpstan-ignore-line
} catch (BindingResolutionException) {
$this->presenter = new Presenter($input, $output);
$this->app->singleton(PresenterContract::class, fn (Application $app) => $this->presenter);
}
$choice = select($question, $options, $default);

assert(is_string($choice));

return $choice;
}

return parent::execute($input, $output);
/** Initiate a progress bar. */
protected function progressStart(int $size): void
{
$this->output->progressStart($size);
}

/** Advance a progress bar. */
protected function progressAdvance(int $step = 1): void
{
$this->output->progressAdvance($step);
}

/** Complete a progress bar. */
protected function progressComplete(): void
{
$this->output->progressFinish();
}
}
22 changes: 11 additions & 11 deletions app/Commands/Process.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,25 @@ public function handle(
assert(is_string($spreadsheet));

if (! is_file($spreadsheet)) {
$this->presenter->error(sprintf('No spreadsheet could be found at %s', $spreadsheet));
$this->failure(sprintf('No spreadsheet could be found at %s', $spreadsheet));

return self::INVALID;
}

try {
$database->prepare();
} catch (DatabaseManagerException $exception) {
$this->presenter->error(sprintf('Database error: %s', $exception->getMessage()));
$this->failure(sprintf('Database error: %s', $exception->getMessage()));

return self::FAILURE;
}

$this->presenter->info(sprintf('Processing %s...', basename($spreadsheet)));
$this->hint(sprintf('Processing %s...', basename($spreadsheet)));

try {
$this->presenter->progressStart(iterator_count($transactionReader->read($spreadsheet)));
$this->progressStart(iterator_count($transactionReader->read($spreadsheet)));
} catch (TransactionReaderException $exception) {
$this->error($exception->getMessage());
$this->failure($exception->getMessage());

return self::INVALID;
}
Expand All @@ -65,19 +65,19 @@ public function handle(
try {
$transactionProcessor->process($transaction);
} catch (TransactionProcessorException $exception) {
$this->presenter->progressComplete();
$this->error($exception->getMessage());
$this->progressComplete();
$this->failure($exception->getMessage());

return self::INVALID;
}

$this->presenter->progressAdvance();
$this->progressAdvance();
}

$this->presenter->progressComplete();
$this->progressComplete();

$this->presenter->success('Transactions successfully processed!');
$this->success('Transactions successfully processed!');

return $commandRunner->run('review');
return $commandRunner->run(command: 'review', output: $this->output);
}
}
41 changes: 34 additions & 7 deletions app/Commands/Review.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@

final class Review extends Command
{
public const SUMMARY_HEADERS = [
'Proceeds',
'Cost basis',
'Non-attributable allowable cost',
'Total cost basis',
'Capital gain or loss',
'Income',
];

/**
* The signature of the command.
*
Expand All @@ -34,7 +43,7 @@ public function handle(SummaryRepository $summaryRepository, TaxYearSummaryRepos
try {
$this->validateTaxYear($taxYear);
} catch (TaxYearIdException $exception) {
$this->presenter->error($exception->getMessage());
$this->failure($exception->getMessage());

return self::INVALID;
}
Expand All @@ -49,26 +58,26 @@ public function handle(SummaryRepository $summaryRepository, TaxYearSummaryRepos
}

if (empty($availableTaxYears)) {
$this->presenter->warning('No tax year to review. Please submit transactions first, using the `process` command');
$this->warning('No tax year to review. Please submit transactions first, using the `process` command');

return self::SUCCESS;
}

if (! is_null($taxYear) && ! in_array($taxYear, $availableTaxYears)) {
$this->presenter->warning('This tax year is not available');
$this->warning('This tax year is not available');
$taxYear = null;
}

if (is_null($taxYear)) {
$this->presenter->info(sprintf('Current fiat balance: %s', $summaryRepository->get()?->fiat_balance ?? ''));
$this->hint(sprintf('Current fiat balance: %s', $summaryRepository->get()?->fiat_balance ?? ''));
}

// Order tax years from more recent to older
rsort($availableTaxYears);

$taxYear ??= count($availableTaxYears) === 1
? $availableTaxYears[0]
: $this->presenter->choice('Please select a tax year for details', $availableTaxYears, $availableTaxYears[0]);
: $this->select('Please select a tax year for details', $availableTaxYears, $availableTaxYears[0]);

assert(is_string($taxYear));

Expand All @@ -83,7 +92,7 @@ private function summary(TaxYearSummaryRepository $repository, string $taxYear,

assert($taxYearSummary instanceof TaxYearSummary);

$this->presenter->summary(
$this->displaySummary(
taxYear: $taxYear,
proceeds: (string) $taxYearSummary->capital_gain->proceeds,
costBasis: (string) $taxYearSummary->capital_gain->costBasis,
Expand All @@ -98,7 +107,7 @@ private function summary(TaxYearSummaryRepository $repository, string $taxYear,
return self::SUCCESS;
}

$taxYear = $this->presenter->choice('Review another tax year?', ['No', ...$availableTaxYears], 'No');
$taxYear = $this->select('Review another tax year?', ['No', ...$availableTaxYears], 'No');

if ($taxYear === 'No') {
return self::SUCCESS;
Expand All @@ -107,6 +116,24 @@ private function summary(TaxYearSummaryRepository $repository, string $taxYear,
return $this->summary($repository, $taxYear, $availableTaxYears);
}

/** Display a tax year's summary. */
private function displaySummary(
string $taxYear,
string $proceeds,
string $costBasis,
string $nonAttributableAllowableCost,
string $totalCostBasis,
string $capitalGain,
string $income,
): void {
$this->hint(sprintf('Summary for tax year %s', $taxYear));

$this->table(
self::SUMMARY_HEADERS,
[[$proceeds, $costBasis, $nonAttributableAllowableCost, $totalCostBasis, $capitalGain, $income]],
);
}

/** @throws TaxYearIdException */
private function validateTaxYear(mixed $taxYear): void
{
Expand Down
11 changes: 8 additions & 3 deletions app/Services/CommandRunner/CommandRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@
namespace App\Services\CommandRunner;

use Illuminate\Contracts\Console\Kernel;
use Symfony\Component\Console\Output\OutputInterface;

final readonly class CommandRunner implements CommandRunnerContract
{
public function __construct(private Kernel $artisan)
{
}

/** Run a command. */
public function run(string $command): int
/**
* Run a command.
*
* @param array<mixed,mixed> $parameters
*/
public function run(string $command, array $parameters = [], ?OutputInterface $output = null): int
{
return $this->artisan->call($command);
return $this->artisan->call($command, $parameters, $output);
}
}
10 changes: 8 additions & 2 deletions app/Services/CommandRunner/CommandRunnerContract.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@

namespace App\Services\CommandRunner;

use Symfony\Component\Console\Output\OutputInterface;

interface CommandRunnerContract
{
/** Run a command. */
public function run(string $command): int;
/**
* Run a command.
*
* @param array<mixed,mixed> $parameters
*/
public function run(string $command, array $parameters = [], ?OutputInterface $output = null): int;
}
93 changes: 0 additions & 93 deletions app/Services/Presenter/Presenter.php

This file was deleted.

Loading

0 comments on commit 602bdce

Please sign in to comment.