diff --git a/README.md b/README.md index c806228..c39818d 100644 --- a/README.md +++ b/README.md @@ -230,12 +230,33 @@ use Savks\Negotiator\Support\DTO\{ new Intersection( new UserMapper($this->user), - new ObjectValue( $this->user, fn (Factory $factory) => [ + new ObjectValue($this->user, fn (Factory $factory) => [ 'otherField' => $factory->string('other_field') ]), ); ``` +* `oneOfConst` — дозволяє вказати, що значення може набувати одного з типів-констант. Приклад: + +```php +source, fn (Factory $factory) => [ + 'field' => $factory->oneOfConst([ + $factory->constNumber(1), + $factory->constNumber(2), + $factory->constNumber(3), + ]), +]); +``` + ## Генерація типів Для генерації типів пакет містить клас генератора `Savks\Negotiator\Support\TypeGeneration\Generator`. Для роботи якого достатньо вказання для яких маперів і з якими просторами імен потрібно згенерувати код. Приклад використання: diff --git a/src/Support/DTO/Castable.php b/src/Support/DTO/Castable.php index 6e1b0f9..ec103fc 100644 --- a/src/Support/DTO/Castable.php +++ b/src/Support/DTO/Castable.php @@ -153,4 +153,12 @@ public function null(): NullValue { return new NullValue(); } + + /** + * @param list $values + */ + public function oneOfConst(array $values, string|Closure|null $accessor = null): OneOfConstValue + { + return new OneOfConstValue($this->source, $values, $accessor); + } } diff --git a/src/Support/DTO/ConstBooleanValue.php b/src/Support/DTO/ConstBooleanValue.php index 6ada98f..b5e5b88 100644 --- a/src/Support/DTO/ConstBooleanValue.php +++ b/src/Support/DTO/ConstBooleanValue.php @@ -7,7 +7,10 @@ ConstBooleanType }; -class ConstBooleanValue extends Value +/** + * @extends ConstValue + */ +class ConstBooleanValue extends ConstValue { public function __construct( protected readonly bool $value, @@ -15,6 +18,11 @@ public function __construct( ) { } + public function originalValue(): bool + { + return $this->value; + } + protected function finalize(): bool { return $this->value; diff --git a/src/Support/DTO/ConstEnumValue.php b/src/Support/DTO/ConstEnumValue.php index 8a01598..38cd414 100644 --- a/src/Support/DTO/ConstEnumValue.php +++ b/src/Support/DTO/ConstEnumValue.php @@ -3,9 +3,10 @@ namespace Savks\Negotiator\Support\DTO; use BackedEnum; +use ReflectionEnum; +use ReflectionNamedType; use Savks\Negotiator\Contexts\TypeGenerationContext; use Savks\PhpContexts\Context; -use StringBackedEnum; use Savks\Negotiator\Support\Types\{ AliasType, @@ -13,12 +14,17 @@ StringType }; -class ConstEnumValue extends NullableValue +class ConstEnumValue extends ConstValue { - public function __construct(protected BackedEnum $case) + public function __construct(protected readonly BackedEnum $case) { } + public function originalValue(): BackedEnum + { + return $this->case; + } + protected function finalize(): string|int|null { return $this->case->value; @@ -26,14 +32,18 @@ protected function finalize(): string|int|null protected function types(): AliasType|StringType|NumberType { - $enumRef = Context::use(TypeGenerationContext::class)->resolveEnumRef( - $this->case::class - ); + /** @var TypeGenerationContext $typeGenerationContext */ + $typeGenerationContext = Context::use(TypeGenerationContext::class); + + $enumRef = $typeGenerationContext->resolveEnumRef($this->case::class); if ($enumRef) { return new AliasType("{$enumRef}.{$this->case->name}"); } - return $this->case instanceof StringBackedEnum ? new StringType() : new NumberType(); + /** @var ReflectionNamedType $type */ + $type = (new ReflectionEnum($this->case))->getBackingType(); + + return $type->getName() === 'string' ? new StringType() : new NumberType(); } } diff --git a/src/Support/DTO/ConstNumberValue.php b/src/Support/DTO/ConstNumberValue.php index 7c4695f..cc0e61f 100644 --- a/src/Support/DTO/ConstNumberValue.php +++ b/src/Support/DTO/ConstNumberValue.php @@ -7,7 +7,7 @@ NumberType }; -class ConstNumberValue extends Value +class ConstNumberValue extends ConstValue { public function __construct( protected readonly int|float $value, @@ -15,6 +15,11 @@ public function __construct( ) { } + public function originalValue(): int|float + { + return $this->value; + } + protected function finalize(): int|float { return $this->value; diff --git a/src/Support/DTO/ConstStringValue.php b/src/Support/DTO/ConstStringValue.php index bfefb56..2f7a8f0 100644 --- a/src/Support/DTO/ConstStringValue.php +++ b/src/Support/DTO/ConstStringValue.php @@ -7,7 +7,7 @@ StringType }; -class ConstStringValue extends Value +class ConstStringValue extends ConstValue { public function __construct( protected readonly string $value, @@ -15,6 +15,11 @@ public function __construct( ) { } + public function originalValue(): string + { + return $this->value; + } + protected function finalize(): string { return $this->value; diff --git a/src/Support/DTO/ConstValue.php b/src/Support/DTO/ConstValue.php new file mode 100644 index 0000000..6a6ef79 --- /dev/null +++ b/src/Support/DTO/ConstValue.php @@ -0,0 +1,14 @@ +resolveValueFromAccessor( + $this->accessor, + $this->source, + $this->sourcesTrace + ); + + if ($this->accessor && last($this->sourcesTrace) !== $this->source) { + $this->sourcesTrace[] = $this->source; + } + + if ($value === null) { + return null; + } + + $isMatched = null; + + foreach ($this->values as $constValue) { + $isMatched = $constValue->originalValue() === $value; + + if ($isMatched) { + break; + } + } + + if (! $isMatched) { + $types = []; + + foreach ($this->values as $constValue) { + if ($constValue instanceof ConstEnumValue) { + $types[] = 'BackedEnum<' . $constValue->originalValue()::class . '>'; + } else { + foreach ($constValue->compileTypes()->types as $type) { + $types[] = match (true) { + $type instanceof ConstBooleanType => $type->value ? 'true' : 'false', + + $type instanceof ConstNumberType, $type instanceof ConstStringType => $type->value, + + $type instanceof BooleanType => 'bool', + $type instanceof NumberType => 'numeric', + $type instanceof StringType => 'string', + + default => throw new RuntimeException( + \sprintf('Unprocessed type "%s".', $type::class) + ) + }; + } + } + } + + throw new UnexpectedValue($types, $value); + } + + return $value instanceof BackedEnum ? $value->value : $value; + } + + protected function types(): Types + { + $types = []; + + foreach ($this->values as $value) { + $types[] = $value->compileTypes()->types; + } + + return new Types( + \array_merge(...$types) + ); + } +}