Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 11 additions & 50 deletions src/core/etl/src/Flow/ETL/Row/EntryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,18 @@ enum_entry,
uuid_entry,
xml_element_entry,
xml_entry};
use function Flow\Types\DSL\{type_date,
type_datetime,
type_html,
type_json,
type_optional,
type_string,
type_time,
type_uuid,
type_xml,
type_xml_element};
use Flow\ETL\Exception\{InvalidArgumentException,
RuntimeException,
SchemaDefinitionNotFoundException};
use function Flow\Types\DSL\{type_optional, type_string};
use Flow\ETL\Exception\{InvalidArgumentException, SchemaDefinitionNotFoundException};
use Flow\ETL\Row\Entry\{ListEntry, MapEntry, StringEntry, StructureEntry};
use Flow\ETL\Schema;
use Flow\ETL\Schema\{Definition, Metadata};
use Flow\Types\Exception\CastingException;
use Flow\Types\Type;
use Flow\Types\Type\Logical\{DateTimeType,
DateType,
HTMLType,
InstanceOfType,
InstanceOfTypeNarrower,
JsonType,
ListType,
MapType,
Expand All @@ -52,7 +43,6 @@ enum_entry,
UuidType,
XMLElementType,
XMLType};
use Flow\Types\Type\Logical\HTMLType;
use Flow\Types\Type\Native\{
ArrayType,
BooleanType,
Expand All @@ -63,17 +53,15 @@ enum_entry,
StringType,
UnionType
};
use Flow\Types\Type\Native\String\StringTypeChecker;
use Flow\Types\Type\Native\String\StringTypeNarrower;
use Flow\Types\Type\TypeDetector;
use Flow\Types\Value\Uuid;

final readonly class EntryFactory
{
/**
* @param null|Definition<mixed>|Schema $schema
*
* @throws InvalidArgumentException
* @throws RuntimeException
* @throws SchemaDefinitionNotFoundException
*
* @return Entry<mixed>
Expand All @@ -97,42 +85,11 @@ public function create(string $entryName, mixed $value, Schema|Definition|null $
$valueType = (new TypeDetector())->detectType($value);

if ($valueType instanceof StringType) {
$value = type_string()->assert($value);
$stringChecker = new StringTypeChecker($value);

if ($stringChecker->isJson()) {
$valueType = type_json();
} elseif ($stringChecker->isUuid()) {
$valueType = type_uuid();
} elseif ($stringChecker->isHTML()) {
$valueType = type_html();
} elseif ($stringChecker->isXML()) {
$valueType = type_xml();
}
$valueType = StringTypeNarrower::narrow($value);
}

if ($valueType instanceof InstanceOfType) {
if ($valueType->class === \DOMDocument::class) {
$valueType = type_xml();
} elseif ($valueType->class === \DOMElement::class) {
$valueType = type_xml_element();
} elseif ($valueType->class === \DateInterval::class) {
$valueType = type_time();
} elseif (\in_array($valueType->class, [\DateTimeImmutable::class, \DateTimeInterface::class, \DateTime::class], true)) {
if ($value instanceof \DateTimeInterface && $value->format('H:i:s') === '00:00:00') {
$valueType = type_date();
} else {
$valueType = type_datetime();
}
} else {
foreach (['Ramsey\Uuid\UuidInterface', Uuid::class, 'Symfony\Component\Uid\Uuid'] as $uuidClass) {
if (\is_a($valueType->class, $uuidClass, true)) {
$valueType = type_uuid();

break;
}
}
}
$valueType = InstanceOfTypeNarrower::narrow($value);
}

return $this->createAs($entryName, $value, $valueType);
Expand Down Expand Up @@ -223,6 +180,10 @@ public function createAs(string $entryName, mixed $value, Definition|Type $defin
return string_entry($entryName, type_optional(type_string())->cast($value), $metadata);
}

if ($type instanceof NullType) {
return StringEntry::fromNull($entryName, $metadata);
}

if ($type instanceof EnumType) {
$castValue = type_optional($type)->cast($value);

Expand Down
12 changes: 10 additions & 2 deletions src/core/etl/tests/Flow/ETL/Tests/Unit/Row/EntryFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ enum_schema,
time_schema,
uuid_schema,
xml_schema};
use function Flow\Types\DSL\{type_datetime, type_float, type_integer, type_list, type_map, type_string, type_structure, type_time_zone};
use function Flow\Types\DSL\{type_datetime, type_float, type_integer, type_list, type_map, type_null, type_string, type_structure, type_time_zone};
use Flow\ETL\Exception\{InvalidArgumentException, SchemaDefinitionNotFoundException};
use Flow\ETL\Row\Entry\TimeEntry;
use Flow\ETL\Row\Entry\{StringEntry, TimeEntry};
use Flow\ETL\Row\{Entry, EntryFactory};
use Flow\ETL\Schema\Metadata;
use Flow\ETL\Tests\Fixtures\Enum\BackedIntEnum;
Expand Down Expand Up @@ -418,6 +418,14 @@ public function test_nested_structure() : void
);
}

public function test_null_type_handled() : void
{
self::assertEquals(
StringEntry::fromNull('e'),
$this->entryFactory->createAs('e', null, type_null())
);
}

public function test_object() : void
{
$this->expectExceptionMessage("e: object<ArrayIterator> can't be converted to any known Entry, please normalize that object first");
Expand Down
51 changes: 6 additions & 45 deletions src/lib/types/src/Flow/Types/Type/AutoCaster.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,9 @@

namespace Flow\Types\Type;

use function Flow\Types\DSL\{get_type,
type_boolean,
type_date,
type_datetime,
type_float,
type_integer,
type_json,
type_time_zone,
type_uuid};
use Flow\Types\Type\Native\String\StringTypeChecker;
use function Flow\Types\DSL\{get_type, type_float};
use Flow\Types\Type\Native\NullType;
use Flow\Types\Type\Native\String\StringTypeNarrower;

final readonly class AutoCaster
{
Expand Down Expand Up @@ -62,44 +55,12 @@ private function castArray(array $value) : array

private function castToString(string $value) : mixed
{
$typeChecker = new StringTypeChecker($value);
$narrowedType = StringTypeNarrower::narrow($value);

if ($typeChecker->isNull()) {
if ($narrowedType instanceof NullType) {
return null;
}

if ($typeChecker->isInteger()) {
return type_integer()->cast($value);
}

if ($typeChecker->isFloat()) {
return type_float()->cast($value);
}

if ($typeChecker->isBoolean()) {
return type_boolean()->cast($value);
}

if ($typeChecker->isJson()) {
return type_json()->cast($value);
}

if ($typeChecker->isUuid()) {
return type_uuid()->cast($value);
}

if ($typeChecker->isTimeZone()) {
return type_time_zone()->cast($value);
}

if ($typeChecker->isDate()) {
return type_date()->cast($value);
}

if ($typeChecker->isDateTime()) {
return type_datetime()->cast($value);
}

return $value;
return $narrowedType->cast($value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Flow\Types\Type\Logical;

use function Flow\Types\DSL\{get_type, type_uuid};
use Flow\Types\Type;
use Flow\Types\Type\TypeNarrower;

final readonly class InstanceOfTypeNarrower implements TypeNarrower
{
/**
* @return Type<mixed>
*/
public static function narrow(mixed $value) : Type
{
if (!\is_object($value)) {
return get_type($value);
}

$valueClass = $value::class;

foreach (['Ramsey\Uuid\UuidInterface', 'Symfony\Component\Uid\Uuid'] as $uuidClass) {
if (\is_a($valueClass, $uuidClass, true)) {
return type_uuid();
}
}

return get_type($value);
}
}
4 changes: 2 additions & 2 deletions src/lib/types/src/Flow/Types/Type/Logical/JsonType.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use function Flow\Types\DSL\type_json;
use Flow\Types\Exception\{CastingException, InvalidTypeException};
use Flow\Types\Type;
use Flow\Types\Type\Native\String\StringTypeChecker;
use Flow\Types\Type\Native\String\StringTypeNarrower;

/**
* @implements Type<string>
Expand Down Expand Up @@ -46,7 +46,7 @@ public function isValid(mixed $value) : bool
return false;
}

return (new StringTypeChecker($value))->isJson();
return StringTypeNarrower::narrow($value) instanceof self;
}

public function normalize() : array
Expand Down
Loading
Loading