Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Laravel Prompts #63

Merged
merged 2 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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