diff --git a/src/Functions/console.php b/src/Functions/console.php index 3b93aa2..8dd959b 100644 --- a/src/Functions/console.php +++ b/src/Functions/console.php @@ -79,7 +79,9 @@ function readLine(string $prompt, $stream = null): IO */ function printError(string $message): IO { - return new IO(fn () => print("\033[31mError: {$message}\033[0m" . PHP_EOL)); + return new IO(function () use ($message): void { + print("\033[31mError: {$message}\033[0m" . PHP_EOL); + }); } const printWarning = '\Phunkie\Effect\Functions\console\printWarning'; @@ -92,7 +94,9 @@ function printError(string $message): IO */ function printWarning(string $message): IO { - return new IO(fn () => print("\033[33mWarning: {$message}\033[0m" . PHP_EOL)); + return new IO(function () use ($message): void { + print("\033[33mWarning: {$message}\033[0m" . PHP_EOL); + }); } const printSuccess = '\Phunkie\Effect\Functions\console\printSuccess'; @@ -105,7 +109,9 @@ function printWarning(string $message): IO */ function printSuccess(string $message): IO { - return new IO(fn () => print("\033[32mSuccess: {$message}\033[0m" . PHP_EOL)); + return new IO(function () use ($message): void { + print("\033[32mSuccess: {$message}\033[0m" . PHP_EOL); + }); } const printInfo = '\Phunkie\Effect\Functions\console\printInfo'; @@ -118,7 +124,9 @@ function printSuccess(string $message): IO */ function printInfo(string $message): IO { - return new IO(fn () => print("\033[36mInfo: {$message}\033[0m" . PHP_EOL)); + return new IO(function () use ($message): void { + print("\033[36mInfo: {$message}\033[0m" . PHP_EOL); + }); } const printDebug = '\Phunkie\Effect\Functions\console\printDebug'; @@ -131,7 +139,9 @@ function printInfo(string $message): IO */ function printDebug(string $message): IO { - return new IO(fn () => print("\033[35mDebug: {$message}\033[0m" . PHP_EOL)); + return new IO(function () use ($message): void { + print("\033[35mDebug: {$message}\033[0m" . PHP_EOL); + }); } const printTable = '\Phunkie\Effect\Functions\console\printTable'; diff --git a/src/IO/IOApp/OptionDefinition.php b/src/IO/IOApp/OptionDefinition.php index fd70220..3071701 100644 --- a/src/IO/IOApp/OptionDefinition.php +++ b/src/IO/IOApp/OptionDefinition.php @@ -56,8 +56,12 @@ public function __construct(string $p1, string|null $p2 = null, string|OptionFor } } - if ($short === null && $long === null) { - throw new \InvalidArgumentException("At least one of short or long name must be provided"); + // Emptiness rather than absence: the name is a required string and every + // branch above assigns it to one of the two, so neither can be null and + // a test for that never fired. What it meant to catch is an option no + // one could ever pass. + if (($short ?? '') === '' && ($long ?? '') === '') { + throw new \InvalidArgumentException('At least one of short or long name must be provided.'); } $this->short = $short; diff --git a/tests/Unit/IO/IOApp/OptionsTest.php b/tests/Unit/IO/IOApp/OptionsTest.php index e027242..384c5fa 100644 --- a/tests/Unit/IO/IOApp/OptionsTest.php +++ b/tests/Unit/IO/IOApp/OptionsTest.php @@ -30,6 +30,19 @@ public function test_it_creates_options_with_mixed_definitions() $this->assertInstanceOf(Options::class, $validation->toOption()->get()); } + // An option has to be callable by something. The guard for that could never + // fire, because the name is a required string and every branch assigns it, + // so an option with no usable name was handed back as if it were fine. + public function test_it_refuses_an_option_with_no_name() + { + $this->assertTrue(option('')->isLeft()); + } + + public function test_it_accepts_an_option_named_only_by_its_long_form() + { + $this->assertTrue(option('', 'verbose', 'Verbose', NoInput)->isRight()); + } + public function test_it_parses_short_flags() { $options = arguments(