Validation: add AssociativeListValidator

+ fix template declarations
master 3.13.0
Julien Rosset 1 week ago
parent ba27ae5963
commit 761067e48a

@ -7,7 +7,7 @@ use DateTimeInterface;
/** /**
* An argument/option value validator expecting a date and/or time * An argument/option value validator expecting a date and/or time
* *
* @template-implements BasedValidator<RegexValidator> * @extends BasedValidator<DateTimeInterface, RegexValidator>
*/ */
abstract class AbstractDateTimeValidator extends BasedValidator { abstract class AbstractDateTimeValidator extends BasedValidator {
/** /**

@ -0,0 +1,75 @@
<?php
namespace jrosset\CliProgram\Validation\Validators;
use Arrayy\Type\StringCollection;
use Closure;
/**
* An argument/option value validator based on an associative list of value
*
* Key = User input
* <br>Value = Real value (see {@see IValidator::getValue() getValue()})
*
* @implements IValidatorWithSuggestions<string>
*/
class AssociativeListValidator implements IValidatorWithSuggestions {
use TIdenticalValidDefaultValidator;
/**
* @use TInternalValueValidator<string>
*/
use TInternalValueValidator;
/**
* @var StringCollection The list of allowed values
*/
private StringCollection $allowedValues;
/**
* Create a validator
*
* @param StringCollection|null $allowedValues The list of allowed values
*/
public function __construct (?StringCollection $allowedValues = null) {
$this->setAllowedValues($allowedValues ?? new StringCollection());
}
/**
* @inheritDoc
*/
public function validate (mixed $value): bool {
if ($this->allowedValues->keyExists($value)) {
$this->setValue($this->allowedValues->get($value));
return true;
}
return false;
}
/**
* The list of allowed values
*
* @return StringCollection The list of allowed values
*/
public function getAllowedValues (): StringCollection {
return $this->allowedValues;
}
/**
* Set the list of allowed values
*
* @param StringCollection $allowedValues The list of allowed values
*
* @return $this
*/
public function setAllowedValues (StringCollection $allowedValues): self {
$this->allowedValues = $allowedValues;
return $this;
}
/**
* @inheritDoc
*/
public function getSuggestions (): Closure|array {
return $this->getAllowedValues()->getKeys()->toArray();
}
}

@ -5,7 +5,10 @@ namespace jrosset\CliProgram\Validation\Validators;
/** /**
* An argument/option value validator based on another, internal, validator * An argument/option value validator based on another, internal, validator
* *
* @template TValidator of IValidator * @template TValue of mixed The type of the returned value
* @template TValidator of IValidator The type of the internal validator
*
* @extends IValidator<TValue>
*/ */
abstract class BasedValidator implements IValidator { abstract class BasedValidator implements IValidator {
/** /**

@ -7,8 +7,6 @@ use DateTimeInterface;
/** /**
* An argument/option value validator expecting a date and time * An argument/option value validator expecting a date and time
*
* @template-implements BasedValidator<RegexValidator>
*/ */
class DateTimeValidator extends AbstractDateTimeValidator { class DateTimeValidator extends AbstractDateTimeValidator {
/** /**

@ -7,8 +7,6 @@ use DateTimeInterface;
/** /**
* An argument/option value validator expecting a date (without time) * An argument/option value validator expecting a date (without time)
*
* @template-implements BasedValidator<RegexValidator>
*/ */
class DateValidator extends AbstractDateTimeValidator { class DateValidator extends AbstractDateTimeValidator {
/** /**

@ -7,11 +7,14 @@ use InvalidArgumentException;
/** /**
* An argument/option value validator expecting a decimal * An argument/option value validator expecting a decimal
* *
* @template-implements BasedValidator<RegexValidator> * @extends BasedValidator<float, RegexValidator>
* @template-implements TInternalValueValidator<float>
*/ */
class DecimalValidator extends BasedValidator { class DecimalValidator extends BasedValidator {
use TIdenticalValidDefaultValidator; use TIdenticalValidDefaultValidator;
/**
* @use TInternalValueValidator<float>
*/
use TInternalValueValidator; use TInternalValueValidator;
/** /**
@ -47,8 +50,8 @@ class DecimalValidator extends BasedValidator {
parent::__construct( parent::__construct(
new RegexValidator( new RegexValidator(
/** @lang PhpRegExp */ '/^(?<int>[+-]?(?:\d{1,3}' . $this->thousandsSeparator . '?(?:\d{3}' . $this->thousandsSeparator . '?)*\d{3}|\d{1,3}))(?:' . $this->decimalSeparator /** @lang PhpRegExp */
. '(?<dec>\d+))?$/' "/^(?<int>[+-]?(?:\\d{1,3}$this->thousandsSeparator?(?:\\d{3}$this->thousandsSeparator?)*\\d{3}|\\d{1,3}))(?:$this->decimalSeparator(?<dec>\\d+))?\$/"
) )
); );
} }

@ -11,8 +11,13 @@ use Symfony\Component\Console\Completion\CompletionInput;
/** /**
* An argument/option value validator for a directory path * An argument/option value validator for a directory path
*
* @implements IValidatorWithSuggestions<string>
*/ */
class DirectoryValidator implements IValidatorWithSuggestions { class DirectoryValidator implements IValidatorWithSuggestions {
/**
* @use TInternalValueValidator<string>
*/
use TInternalValueValidator; use TInternalValueValidator;
/** /**
@ -80,7 +85,7 @@ class DirectoryValidator implements IValidatorWithSuggestions {
return false; return false;
} }
$this->setValue($value); $this->setValue((string)$value);
return true; return true;
} }

@ -4,11 +4,17 @@ namespace jrosset\CliProgram\Validation\Validators;
/** /**
* An argument/option value validator expecting an email address * An argument/option value validator expecting an email address
*
* @implements IValidator<string>
*/ */
class EmailValidator implements IValidator { class EmailValidator implements IValidator {
use TInternalValueValidator;
use TIdenticalValidDefaultValidator; use TIdenticalValidDefaultValidator;
/**
* @use TInternalValueValidator<string>
*/
use TInternalValueValidator;
/** /**
* @inheritDoc * @inheritDoc
*/ */

@ -13,8 +13,9 @@ use UnitEnum;
* An argument/option value validator based on an enumeration * An argument/option value validator based on an enumeration
* *
* @template TEnum of UnitEnum * @template TEnum of UnitEnum
* @template-implements IValidator<TEnum> *
* @template-implements TInternalValueValidator<TEnum> * @extends BasedValidator<TEnum, ListValidator>
* @implements IValidatorWithSuggestions<TEnum>
*/ */
class EnumValidator extends BasedValidator implements IValidatorWithSuggestions { class EnumValidator extends BasedValidator implements IValidatorWithSuggestions {
/** /**
@ -53,10 +54,14 @@ class EnumValidator extends BasedValidator implements IValidatorWithSuggestions
/** /**
* @inheritDoc * @inheritDoc
* *
* @return TEnum|null The value
*
* @throws ReflectionException * @throws ReflectionException
*/ */
public function getValue (): UnitEnum { public function getValue (): ?UnitEnum {
$enumCase = $this->getInternalValidator()->getValue(); if (($enumCase = $this->getInternalValidator()->getValue()) === null) {
return null;
}
return $this->enum->getCase($enumCase)->getValue(); return $this->enum->getCase($enumCase)->getValue();
} }

@ -5,7 +5,7 @@ namespace jrosset\CliProgram\Validation\Validators;
use Arrayy\Collection\AbstractCollection; use Arrayy\Collection\AbstractCollection;
/** /**
* A list of options of a filesystem based validator * A list of options of a filesystem-based validator
* *
* @extends AbstractCollection<FilesystemValidationOption> * @extends AbstractCollection<FilesystemValidationOption>
*/ */

@ -5,7 +5,7 @@ namespace jrosset\CliProgram\Validation\Validators;
/** /**
* An argument/option value validator * An argument/option value validator
* *
* @template TValue The Type of the returned value * @template TValue of mixed The type of the returned value
*/ */
interface IValidator { interface IValidator {
/** /**
@ -26,9 +26,9 @@ interface IValidator {
*/ */
public function validate (mixed $value): bool; public function validate (mixed $value): bool;
/** /**
* Get the value, after it's validation * Get the value, after the validation
* *
* @return TValue|null The value * @return TValue|null The value
*/ */
public function getValue (); public function getValue (): mixed;
} }

@ -10,7 +10,8 @@ use Symfony\Component\Console\Completion\Suggestion;
/** /**
* An argument/option value validator * An argument/option value validator
* *
* @template TValue The Type of the returned value * @template TValue of mixed The type of the returned value
*
* @extends IValidator<TValue> * @extends IValidator<TValue>
*/ */
interface IValidatorWithSuggestions extends IValidator { interface IValidatorWithSuggestions extends IValidator {

@ -7,11 +7,14 @@ use InvalidArgumentException;
/** /**
* An argument/option value validator expecting an integer * An argument/option value validator expecting an integer
* *
* @template-implements BasedValidator<RegexValidator> * @extends BasedValidator<int, RegexValidator>
* @template-implements TInternalValueValidator<int>
*/ */
class IntegerValidator extends BasedValidator { class IntegerValidator extends BasedValidator {
use TIdenticalValidDefaultValidator; use TIdenticalValidDefaultValidator;
/**
* @use TInternalValueValidator<int>
*/
use TInternalValueValidator; use TInternalValueValidator;
/** /**
@ -42,7 +45,7 @@ class IntegerValidator extends BasedValidator {
parent::__construct( parent::__construct(
new RegexValidator( new RegexValidator(
/** @lang PhpRegExp */ '/^[+-]?(?:\d{1,3}' . $this->thousandsSeparator . '?(?:\d{3}' . $this->thousandsSeparator . '?)*\d{3}|\d{1,3})$/' /** @lang PhpRegExp */ "/^[+-]?(?:\\d{1,3}$this->thousandsSeparator?(?:\\d{3}$this->thousandsSeparator?)*\\d{3}|\\d{1,3})\$/"
) )
); );
} }

@ -3,68 +3,25 @@
namespace jrosset\CliProgram\Validation\Validators; namespace jrosset\CliProgram\Validation\Validators;
use Arrayy\Type\StringCollection; use Arrayy\Type\StringCollection;
use Closure;
/** /**
* An argument/option value validator based on a list of value * An argument/option value validator based on a list of value
*
* @template TValue
* @template-implements IValidator<TValue>
* @template-implements TInternalValueValidator<TValue>
*/
class ListValidator implements IValidatorWithSuggestions {
use TIdenticalValidDefaultValidator;
use TInternalValueValidator;
/**
* @var StringCollection The list of allowed values
*/ */
private StringCollection $allowedValues; class ListValidator extends AssociativeListValidator {
/** /**
* Create a validator * Create a validator
* *
* @param StringCollection|null $allowedValues The list of allowed values * @param StringCollection|null $allowedValues The list of allowed values
*/ */
public function __construct (?StringCollection $allowedValues = null) { public function __construct (?StringCollection $allowedValues = null) {
$this->setAllowedValues($allowedValues ?? new StringCollection()); //region Rebuild the allowed values collection with key = value
} $realAllowedValues = new StringCollection();
if ($allowedValues !== null) {
/** foreach ($allowedValues as $value) {
* @inheritDoc $realAllowedValues->add($value, $value);
*/
public function validate (mixed $value): bool {
if ($this->getAllowedValues()->contains($value)) {
$this->setValue($value);
return true;
}
return false;
}
/**
* The list of allowed values
*
* @return StringCollection The list of allowed values
*/
public function getAllowedValues (): StringCollection {
return $this->allowedValues;
} }
/**
* Set the list of allowed values
*
* @param StringCollection $allowedValues The list of allowed values
*
* @return $this
*/
public function setAllowedValues (StringCollection $allowedValues): self {
$this->allowedValues = $allowedValues;
return $this;
} }
//endregion
/** parent::__construct($realAllowedValues);
* @inheritDoc
*/
public function getSuggestions (): Closure|array {
return $this->getAllowedValues()->toArray();
} }
} }

@ -4,6 +4,8 @@ namespace jrosset\CliProgram\Validation\Validators;
/** /**
* An argument/option value validator based on a regex * An argument/option value validator based on a regex
*
* @implements IValidator<string>
*/ */
class RegexValidator implements IValidator { class RegexValidator implements IValidator {
use TIdenticalValidDefaultValidator; use TIdenticalValidDefaultValidator;
@ -87,6 +89,8 @@ class RegexValidator implements IValidator {
/** /**
* @inheritDoc * @inheritDoc
*
* @return string|null The value or Null if not set
*/ */
public function getValue (): ?string { public function getValue (): ?string {
return $this->getMatch(static::GROUP_VALUE) ?? $this->getMatch(0); return $this->getMatch(static::GROUP_VALUE) ?? $this->getMatch(0);

@ -8,31 +8,30 @@ namespace jrosset\CliProgram\Validation\Validators;
* *
* Call {@see self::setValue()} to set the value * Call {@see self::setValue()} to set the value
* *
* @template TValue * @template TValue of mixed
* @template-implements IValidator<TValue>
*/ */
trait TInternalValueValidator { trait TInternalValueValidator {
/** /**
* @var TValue|null The current value, after it's validation * @var TValue|null The current value, after the validation
*/ */
private $value = null; private mixed $value = null;
/** /**
* Get the value, after it's validation * Get the value, after the validation
* *
* @return TValue|null The value * @return TValue|null The value
*/ */
public function getValue () { public function getValue (): mixed {
return $this->value; return $this->value;
} }
/** /**
* Set the value * Set the value
* *
* @param TValue $value The value (after it's validation) * @param TValue $value The value (after the validation)
* *
* @return $this * @return $this
*/ */
protected function setValue ($value): static { protected function setValue (mixed $value): static {
$this->value = $value; $this->value = $value;
return $this; return $this;
} }

@ -7,8 +7,6 @@ use DateTimeInterface;
/** /**
* An argument/option value validator expecting a date (without time) * An argument/option value validator expecting a date (without time)
*
* @template-implements BasedValidator<RegexValidator>
*/ */
class TimeValidator extends AbstractDateTimeValidator { class TimeValidator extends AbstractDateTimeValidator {
/** /**

Loading…
Cancel
Save