Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[type] introduce converted type. #405

Merged
merged 1 commit into from
May 17, 2023
Merged

[type] introduce converted type. #405

merged 1 commit into from
May 17, 2023

Conversation

veewee
Copy link
Collaborator

@veewee veewee commented Apr 27, 2023

This PR introduces a new type that can be used to convert a given input into an expected output whilst coecing.
Example use-cases:

  • You receive a date as a formatted string, but want to use it as DateTimeImmutable in your code.
  • You receive an email as string, but want to work with an Email value object.
  • You want to validate some basic formatting validations on the provided input
  • You want to split a string into multiple parts
  • ...

As a reference, Zod (a typescript equivalent of psl\type), has this:
https://zod.dev/?id=preprocess

This PR introduces:

converted<I, O>(
    TypeInterface<I> $from,
    TypeInterface<O> $into,
    (Closure(I): O) $converter
): Typeinterface<O>

It can be used like this:

$dateTimeType = Type\converted(
    Type\string(),
    Type\instance_of(DateTimeImmutable::class),
    static function (string $value): DateTimeImmutable {
        $date = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $value);
        if (!$date) {
           // Exceptions will be transformed to CoerceException
            throw new \RuntimeException('Invalid format given. Expected date to be of format {format}');
        }

        return $date;
    }
);

$emailType = Type\converted(
    Type\string(),
    Type\instance_of(EmailValueObject::class),
    static function (string $value): EmailValueObject {
        // Exceptions will be transformed to CoerceException
        return EmailValueObject::tryParse($value);
    }
);

$shape = shape([
    'email' => $emailType,
    'dateTime' => $dateTimeType
]);

$coerced = $shape->coerce($data); // Coerce will convert from -> into, If the provided value is already into - it will skip conversion.
$shape->assert($coerced); // Assert will check if the value is of the type it converts into!

Example CoerceException:

Could not coerce "string" to type "DateTimeImmutable": Invalid format given. Expected date to be of format {format}

@coveralls
Copy link

coveralls commented Apr 27, 2023

Pull Request Test Coverage Report for Build 4818281220

  • 24 of 24 (100.0%) changed or added relevant lines in 3 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.005%) to 99.155%

Totals Coverage Status
Change from base Build 4447203159: 0.005%
Covered Lines: 3992
Relevant Lines: 4026

💛 - Coveralls

return $value;
}

$coercedInput = $this->from->coerce($value);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we try to coerce it into the into type first? maybe that is possible, even if it doesn't match

Copy link
Collaborator Author

@veewee veewee Apr 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'dd say that would be a strange limitation. If you explicitly pass a converter function, it is strange to do some auto-conversion internal first?

For example: It would also block this case:

if you want to have a string that has a specific format, you could parse it like this now:

$scalarEmailType = Type\converted(
    Type\string(),
    Type\non_empty_string(),
    static function (string $value): string {
         if (!Regex\matches($value, $emailPattern)) {
               throw new \RuntimeException('The provided email is not valid');
         }
         return $value;
    }
);

Copy link
Collaborator Author

@veewee veewee Apr 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way, it could solve scenarios like these:
#404

$customBoolType = Type\converted(
    Type\string(),
    Type\bool(),
    static function (string $value): bool {
        return !in_array($value, ['', 'False', 'No', '0'], true);
    }
);

@azjezz azjezz changed the title Introduce a "converted" type. [type] introduce converted type. May 17, 2023
@veewee veewee added Priority: Medium This issue may be useful, and needs some attention. Status: Accepted It's clear what the subject of the issue is about, and what the resolution should be. Type: Enhancement Most issues will probably ask for additions or changes. labels May 17, 2023
@veewee veewee added this to the 2.6.0 milestone May 17, 2023
@veewee veewee merged commit ad3159f into azjezz:next May 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Priority: Medium This issue may be useful, and needs some attention. Status: Accepted It's clear what the subject of the issue is about, and what the resolution should be. Type: Enhancement Most issues will probably ask for additions or changes.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants