Skip to content

Commit

Permalink
Arguments\Option : added Enum cases for all defined options for an Ar…
Browse files Browse the repository at this point in the history
…gument #16
  • Loading branch information
rayanlevert committed Aug 17, 2024
1 parent 7f5a54f commit 4debc7d
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 57 deletions.
63 changes: 15 additions & 48 deletions src/Arguments/Argument.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@

namespace RayanLevert\Cli\Arguments;

use function array_key_exists;
use function is_string;
use function is_bool;
use function is_double;
use function is_int;
use function gettype;
use function is_numeric;
use function implode;
Expand Down Expand Up @@ -35,64 +31,35 @@ class Argument
private bool $hasBeenHandled = false;

/**
* Creates an argument with a name and differents options
* Creates an argument with a name and different options
*
* @param array<string, string|bool|int|float> $options
* - description (string) Description of the argument
* - defaultValue (float|int|string) Default value if the argument is not handled
* - required (bool) If the argument must be present and parsed
* - castTo (string) PHP type - If the argument has a type other than string, its value will be casted
* - noValue (bool) If a prefixed argument doesn't need a value -> boolean cast
* - prefix (string) Short prefix (-u)
* - longPrefix (string) Long prefix (--user)
* @param array<string, string|bool|int|float> $options See Arguments\Option cases for more informations
*
* @throws \RayanLevert\Cli\Arguments\Exception If options are incompatible or incorrectes
* @throws \RayanLevert\Cli\Arguments\Exception If options are incompatible or incorrect
*/
final public function __construct(protected readonly string $name, array $options = [])
{
if (array_key_exists('description', $options) && is_string($options['description'])) {
$this->description = $options['description'];
}

if (array_key_exists('required', $options) && is_bool($options['required'])) {
$this->isRequired = $options['required'];
}

if (array_key_exists('noValue', $options) && is_bool($options['noValue'])) {
$this->noValue = $options['noValue'];
}

if (array_key_exists('prefix', $options) && is_string($options['prefix'])) {
$this->prefix = $options['prefix'];
}
foreach ($options as $name => $value) {
if (!($option = Option::tryFrom($name)) || !$option->verifiesType($value)) {
continue;
}

if (array_key_exists('longPrefix', $options) && is_string($options['longPrefix'])) {
$this->longPrefix = $options['longPrefix'];
$this->{$option->getPhpProperty()} = $value;
}

if (array_key_exists('castTo', $options) && is_string($options['castTo'])) {
$this->castTo = match ($options['castTo']) {
if ($this->castTo) {
$this->castTo = match ($this->castTo) {
'int', 'integer' => 'integer',
'bool', 'boolean' => throw new Exception('castTo cannot be of type bool, use the option "noValue"'),
'double', 'float' => 'double',
'string' => 'string',
default => throw new Exception($options['castTo'] . ' is not a native PHP type')
default => throw new Exception($this->castTo . ' is not a native PHP type')
};
}

if (array_key_exists('defaultValue', $options)) {
$defaultValue = $options['defaultValue'];

if (!is_string($defaultValue) && !is_double($defaultValue) && !is_int($defaultValue)) {
throw new Exception('Default value must be of type float, integer or string');
}

$this->defaultValue = $defaultValue;

// Asserts the default value type is the same as the castTo option
if (gettype($this->defaultValue) !== $this->castTo) {
throw new Exception("Default value is not the same type as castTo option ({$this->castTo})");
}
// Asserts the default value type is the same as the castTo option
if ($this->defaultValue && gettype($this->defaultValue) !== $this->castTo) {
throw new Exception("Default value is not the same type as castTo option ({$this->castTo})");
}

if (($this->noValue || $this->isRequired) && $this->defaultValue) {
Expand Down Expand Up @@ -137,7 +104,7 @@ public function setValueParsed(bool|string $value): void
return;
}

// Thorws an exception if the value is not of casted type
// Throws an exception if the value is not of casted type
if ($this->castTo === 'integer') {
if (!is_numeric($value)) {
throw new ParseException("Argument {$this->name} is not a numeric string (must cast to integer)");
Expand Down
57 changes: 57 additions & 0 deletions src/Arguments/Option.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace RayanLevert\Cli\Arguments;

use function is_string;
use function is_bool;
use function is_float;

/**
* Enumeration of all defined options for Arguments\Argument
*/
enum Option: string
{
/** (string) Description of an argument */
case DESCRIPTION = 'description';

/** (bool) If the argument must be present and parsed */
case REQUIRED = 'required';

/** (bool) If a prefixed argument does not need a value -> verifies only its presence by a boolean status (ex: --help) */
case NO_VALUE = 'noValue';

/** (string) Short prefix (-u=) */
case PREFIX = 'prefix';

/** (string) Long prefix (--user=) */
case LONG_PREFIX = 'longPrefix';

/** (string) PHP type - If the argument has a type other than string -> its value will be casted */
case CAST_TO = 'castTo';

/** (string|int|float) Default value if the argument is not handled */
case DEFAULT_VALUE = 'defaultValue';

/** From a value, verifies the type the option must require */
public function verifiesType(mixed $value): bool
{
return match ($this) {
self::DESCRIPTION => is_string($value),
self::REQUIRED => is_bool($value),
self::NO_VALUE => is_bool($value),
self::PREFIX => is_string($value),
self::LONG_PREFIX => is_string($value),
self::CAST_TO => is_string($value),
self::DEFAULT_VALUE => is_string($value) || is_int($value) || is_float($value)
};
}

/** Returns the php property for the Arguments\Argument class */
public function getPhpProperty(): string
{
return match ($this) {
self::REQUIRED => 'isRequired',
default => $this->value
};
}
}
13 changes: 4 additions & 9 deletions tests/Arguments/ArgumentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,11 @@ public function testDefaultValue(): void
$oArgument = new Argument('test');
$this->assertNull($oArgument->getDefaultValue());

// we recover every incorrect PHP type = throws an exception
// we recover every incorrect PHP type = does not assign the value
foreach ([true, false, [], new \stdClass(), fopen(__FILE__, 'r')] as $incorrectValue) {
try {
$oArgument = new Argument('test', ['defaultValue' => $incorrectValue]);

$this->fail('expected exception pour la valeur ' . var_export($incorrectValue, true));
} catch (\Exception $e) {
$this->assertSame('Default value must be of type float, integer or string', $e->getMessage());
$this->assertInstanceOf(Exception::class, $e);
}
$oArgument = new Argument('test', ['defaultValue' => $incorrectValue]);

$this->assertNull($oArgument->getDefaultValue());
}

// castTo string and defaultValue string OK
Expand Down

0 comments on commit 4debc7d

Please sign in to comment.