From b6af6c0645aa3e396cdf02966849f3d401b61ee1 Mon Sep 17 00:00:00 2001 From: Saif Eddin Gmati <29315886+azjezz@users.noreply.github.com> Date: Mon, 1 Feb 2021 21:09:38 +0100 Subject: [PATCH] [Type] add non-empty-string type (#116) --- src/Psl/Internal/Loader.php | 2 + src/Psl/Iter/flat_map.php | 2 +- src/Psl/Type/Internal/NonEmptyStringType.php | 72 ++++++++++++++++++++ src/Psl/Type/non_empty_string.php | 13 ++++ tests/Psl/Type/NonEmptyStringTypeTest.php | 52 ++++++++++++++ 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/Psl/Type/Internal/NonEmptyStringType.php create mode 100644 src/Psl/Type/non_empty_string.php create mode 100644 tests/Psl/Type/NonEmptyStringTypeTest.php diff --git a/src/Psl/Internal/Loader.php b/src/Psl/Internal/Loader.php index 6c887770..4c474e30 100644 --- a/src/Psl/Internal/Loader.php +++ b/src/Psl/Internal/Loader.php @@ -318,6 +318,7 @@ final class Loader 'Psl\Type\object', 'Psl\Type\resource', 'Psl\Type\string', + 'Psl\Type\non_empty_string', 'Psl\Type\scalar', 'Psl\Type\union', 'Psl\Type\is_array', @@ -432,6 +433,7 @@ final class Loader 'Psl\Type\Internal\ObjectType', 'Psl\Type\Internal\ResourceType', 'Psl\Type\Internal\StringType', + 'Psl\Type\Internal\NonEmptyStringType', 'Psl\Type\Internal\UnionType', 'Psl\Type\Exception\TypeTrace', 'Psl\Type\Exception\AssertException', diff --git a/src/Psl/Iter/flat_map.php b/src/Psl/Iter/flat_map.php index 8fc712ec..ccc46bc2 100644 --- a/src/Psl/Iter/flat_map.php +++ b/src/Psl/Iter/flat_map.php @@ -12,7 +12,7 @@ * @psalm-param iterable $iterable Iterable to be mapped over * @psalm-param (callable(Tv): iterable) $mapper * - * @psalm-return iterable + * @psalm-return iterable */ function flat_map(iterable $iterable, callable $mapper): iterable { diff --git a/src/Psl/Type/Internal/NonEmptyStringType.php b/src/Psl/Type/Internal/NonEmptyStringType.php new file mode 100644 index 00000000..9864b6ea --- /dev/null +++ b/src/Psl/Type/Internal/NonEmptyStringType.php @@ -0,0 +1,72 @@ + + * + * @internal + */ +final class NonEmptyStringType extends Type\Type +{ + /** + * @psalm-param mixed $value + * + * @psalm-return non-empty-string + * + * @throws CoercionException + */ + public function coerce($value): string + { + if (Type\is_string($value) && !Str\is_empty($value)) { + return $value; + } + + if (Type\is_int($value)) { + $str = (string) $value; + if (!Str\is_empty($str)) { + /** @var non-empty-string $str */ + return $str; + } + } + + if (Type\is_object($value) && method_exists($value, '__toString')) { + $str = (string)$value; + if (!Str\is_empty($str)) { + return $str; + } + } + + throw CoercionException::withValue($value, $this->toString(), $this->getTrace()); + } + + /** + * @psalm-param mixed $value + * + * @psalm-return non-empty-string + * + * @psalm-assert non-empty-string $value + * + * @throws AssertException + */ + public function assert($value): string + { + if (Type\is_string($value) && !Str\is_empty($value)) { + return $value; + } + + throw AssertException::withValue($value, $this->toString(), $this->getTrace()); + } + + public function toString(): string + { + return 'non-empty-string'; + } +} diff --git a/src/Psl/Type/non_empty_string.php b/src/Psl/Type/non_empty_string.php new file mode 100644 index 00000000..f4c44160 --- /dev/null +++ b/src/Psl/Type/non_empty_string.php @@ -0,0 +1,13 @@ + + */ +function non_empty_string(): Type +{ + return new Internal\NonEmptyStringType(); +} diff --git a/tests/Psl/Type/NonEmptyStringTypeTest.php b/tests/Psl/Type/NonEmptyStringTypeTest.php new file mode 100644 index 00000000..e11e17da --- /dev/null +++ b/tests/Psl/Type/NonEmptyStringTypeTest.php @@ -0,0 +1,52 @@ + + */ +final class NonEmptyStringTypeTest extends TypeTest +{ + /** + * @return Type\Type + */ + public function getType(): Type\Type + { + return Type\non_empty_string(); + } + + public function getValidCoercions(): iterable + { + yield ['hello', 'hello']; + yield [$this->stringable('hello'), 'hello']; + yield [123, '123']; + yield [0, '0']; + yield ['0', '0']; + yield ['123', '123']; + yield ['1e23', '1e23']; + yield [$this->stringable('123'), '123']; + } + + public function getInvalidCoercions(): iterable + { + yield ['']; + yield [1.0]; + yield [1.23]; + yield [[]]; + yield [[1]]; + yield [Type\bool()]; + yield [null]; + yield [false]; + yield [true]; + yield [STDIN]; + } + + public function getToStringExamples(): iterable + { + yield [$this->getType(), 'non-empty-string']; + } +}