Skip to content

Commit

Permalink
Alternative approach to optional label
Browse files Browse the repository at this point in the history
  • Loading branch information
jessarcher committed Sep 23, 2023
1 parent 9ac9f91 commit 9bd26c8
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 36 deletions.
31 changes: 14 additions & 17 deletions src/Progress.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Throwable;

/**
* @template TReturn of mixed
* @template TSteps of iterable<mixed>|int
*/
class Progress extends Prompt
{
Expand All @@ -30,17 +30,15 @@ class Progress extends Prompt
/**
* Create a new ProgressBar instance.
*
* @template TStep of mixed
*
* @param iterable<TStep>|int $steps
* @param ?Closure(($steps is int ? int : TStep), Progress<TReturn>): TReturn $callback
* @param TSteps $steps
*/
public function __construct(public string $label, public iterable|int $steps, public ?Closure $callback = null, public string $hint = '')
public function __construct(public string $label, public iterable|int $steps, public string $hint = '')
{
$this->total = match (true) {
is_int($this->steps) => $this->steps,
is_countable($this->steps) => count($this->steps),
is_iterable($this->steps) => iterator_count($this->steps),
default => throw new InvalidArgumentException('Unable to count steps.'),
};

if ($this->total === 0) {
Expand All @@ -49,31 +47,28 @@ public function __construct(public string $label, public iterable|int $steps, pu
}

/**
* Display the progress bar.
* Map over the steps while rendering the progress bar.
*
* @template TReturn
*
* @return $this|array<TReturn>
* @param Closure((TSteps is int ? int : value-of<TSteps>), $this): TReturn $callback
* @return array<TReturn>
*/
public function display(): static|array
public function map(Closure $callback): array
{
$this->capturePreviousNewLines();

if ($this->callback === null) {
return $this;
}

$this->start();

$result = [];

try {
if (is_int($this->steps)) {
for ($i = 0; $i < $this->steps; $i++) {
$result[] = ($this->callback)($i, $this);
$result[] = $callback($i, $this);
$this->advance();
}
} else {
foreach ($this->steps as $step) {
$result[] = ($this->callback)($step, $this);
$result[] = $callback($step, $this);
$this->advance();
}
}
Expand Down Expand Up @@ -102,6 +97,8 @@ public function display(): static|array
*/
public function start(): void
{
$this->capturePreviousNewLines();

if (function_exists('pcntl_signal')) {
$this->originalAsync = pcntl_async_signals(true);
pcntl_signal(SIGINT, function () {
Expand Down
2 changes: 1 addition & 1 deletion src/Themes/Default/ProgressRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ProgressRenderer extends Renderer
/**
* Render the progress bar.
*
* @param Progress<mixed> $progress
* @param Progress<int|iterable<mixed>> $progress
*/
public function __invoke(Progress $progress): string
{
Expand Down
31 changes: 24 additions & 7 deletions src/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Closure;
use Illuminate\Support\Collection;
use InvalidArgumentException;

/**
* Prompt the user for text input.
Expand Down Expand Up @@ -165,14 +166,30 @@ function table(array|Collection $headers = [], array|Collection $rows = null): v
/**
* Display a progress bar.
*
* @template TStep of mixed
* @template TReturn of mixed
* @template TSteps of iterable<mixed>|int
* @template TReturn
*
* @param iterable<TStep>|int $steps
* @param ?Closure(($steps is int ? int : TStep), Progress<TReturn>): TReturn $callback
* @return Progress<TReturn>|array<TReturn>
* @param string $label
* @param TSteps $steps
* @param ?Closure((TSteps is int ? int : value-of<TSteps>), Progress<TSteps>): TReturn $callback
* @param string $hint
* @return ($callback is null ? Progress<TSteps> : array<TReturn>)
*/
function progress(iterable|int $steps, string $label = '', Closure $callback = null, string $hint = ''): Progress|array
function progress($label = '', $steps = null, $callback = null, $hint = null): array|Progress
{
return (new Progress($label, $steps, $callback, $hint))->display();
if (! is_string($label)) {
[$label, $steps, $callback, $hint] = ['', $label, $steps, $callback];
}

if ($steps === null) {
throw new InvalidArgumentException('Steps must be an array or integer');
}

$progress = new Progress($label, $steps, $hint ?? '');

if ($callback !== null) {
return $progress->map($callback);
}

return $progress;
}
71 changes: 60 additions & 11 deletions tests/Feature/ProgressTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
expect($result)->toBe(['ALABAMA', 'ALASKA', 'ARIZONA', 'ARKANSAS']);
});

it('renders a progress bar with an item label', function () {
it('can update the label and hint while rendering', function () {
Prompt::fake();

$states = [
Expand All @@ -71,32 +71,79 @@
steps: $states,
callback: function ($item, $progress) {
usleep(1000);
$progress->hint($item);
$progress->label(strtoupper($item));
$progress->hint(strtolower($item));
}
);

Prompt::assertOutputContains('Adding States');

foreach ($states as $state) {
Prompt::assertOutputContains($state);
Prompt::assertOutputContains(strtoupper($state));
Prompt::assertOutputContains(strtolower($state));
}
});

it('renders a progress bar without a label', function () {
it('renders a progress bar without a label using named arguments', function () {
Prompt::fake();

$states = [
'Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado',
];
progress(
steps: 6,
callback: function ($item, $progress) {
usleep(1000);
$progress->hint($item);
}
);

Prompt::assertStrippedOutputContains(<<<'OUTPUT'
┌──────────────────────────────────────────────────────────────┐
│ │
└───────────────────────────────────────────────────────── 0/6 ┘
OUTPUT);
});

it('renders a progress bar with a label using named arguments', function () {
Prompt::fake();

progress(
steps: $states,
label: 'Adding States',
steps: 6,
callback: function ($item, $progress) {
usleep(1000);
$progress->hint($item);
}
);

Prompt::assertStrippedOutputContains(<<<'OUTPUT'
┌ Adding States ───────────────────────────────────────────────┐
│ │
└───────────────────────────────────────────────────────── 0/6 ┘
OUTPUT);
});

it('renders a progress bar with a label using positional arguments', function () {
Prompt::fake();

progress('Adding States', 6, function ($item, $progress) {
usleep(1000);
$progress->hint($item);
});

Prompt::assertStrippedOutputContains(<<<'OUTPUT'
┌ Adding States ───────────────────────────────────────────────┐
│ │
└───────────────────────────────────────────────────────── 0/6 ┘
OUTPUT);
});

it('renders a progress bar without a label using positional arguments', function () {
Prompt::fake();

progress(6, function ($item, $progress) {
usleep(1000);
$progress->hint($item);
});

Prompt::assertStrippedOutputContains(<<<'OUTPUT'
┌──────────────────────────────────────────────────────────────┐
│ │
Expand Down Expand Up @@ -129,7 +176,7 @@
Prompt::assertOutputDoesntContain('Alabama');
});

it('can provide an item label when in manual mode', function () {
it('can update the label and hint in manual mode', function () {
Prompt::fake();

$states = [
Expand All @@ -146,7 +193,8 @@
foreach ($states as $state) {
usleep(1000);
$progress
->hint($state)
->label(strtoupper($state))
->hint(strtolower($state))
->advance();
}

Expand All @@ -155,6 +203,7 @@
Prompt::assertOutputContains('Adding States');

foreach ($states as $state) {
Prompt::assertOutputContains($state);
Prompt::assertOutputContains(strtoupper($state));
Prompt::assertOutputContains(strtolower($state));
}
});

0 comments on commit 9bd26c8

Please sign in to comment.