Skip to content

Commit 9c9458b

Browse files
committed
Add asEnumOf and enumOf functions
1 parent 697b17a commit 9c9458b

File tree

5 files changed

+115
-0
lines changed

5 files changed

+115
-0
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"src/Fp/Functions/Callable/Partial.php",
4646
"src/Fp/Functions/Callable/PartialLeft.php",
4747
"src/Fp/Functions/Callable/PartialRight.php",
48+
"src/Fp/Functions/Cast/AsEnumOf.php",
4849
"src/Fp/Functions/Cast/AsArray.php",
4950
"src/Fp/Functions/Cast/AsBool.php",
5051
"src/Fp/Functions/Cast/AsFloat.php",

src/Fp/Functions/Cast/AsEnumOf.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Fp\Cast;
4+
5+
use Closure;
6+
use BackedEnum;
7+
use Fp\Functional\Option\Option;
8+
9+
/**
10+
* Cast string or int to BackedEnum instance.
11+
*
12+
* ```php
13+
* >>> asEnumOf(1, IntEnum::class)
14+
* => Some(IntEnum(1))
15+
* >>> asEnumOf(42, IntEnum::class)
16+
* => None
17+
* ```
18+
*
19+
* @template TEnum of BackedEnum
20+
*
21+
* @param class-string<TEnum> $enum
22+
* @return Option<TEnum>
23+
*/
24+
function asEnumOf(mixed $subject, string $enum): Option
25+
{
26+
if (!is_string($subject) && !is_int($subject)) {
27+
return Option::none();
28+
}
29+
30+
return Option::try(fn() => $enum::from($subject));
31+
}
32+
33+
/**
34+
* Curried version of {@see asEnumOf}.
35+
*
36+
* ```php
37+
* >>> asEnumOf(IntEnum::class)(1)
38+
* => Some(IntEnum(1))
39+
* >>> asEnumOf(IntEnum::class)(42)
40+
* => None
41+
* ```
42+
*
43+
* @template TEnum of BackedEnum
44+
*
45+
* @param class-string<TEnum> $enum
46+
* @return Closure(mixed): Option<TEnum>
47+
*/
48+
function enumOf(string $enum): Closure
49+
{
50+
return fn(mixed $subject) => asEnumOf($subject, $enum);
51+
}

tests/Mock/IntEnum.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Tests\Mock;
4+
5+
enum IntEnum: int
6+
{
7+
case FST = 1;
8+
case SND = 2;
9+
case THR = 3;
10+
}

tests/Mock/StringEnum.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Tests\Mock;
4+
5+
enum StringEnum: string
6+
{
7+
case FST = 'fst';
8+
case SND = 'snd';
9+
case THR = 'thr';
10+
}

tests/Runtime/Functions/Cast/CastTest.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@
55
namespace Tests\Runtime\Functions\Cast;
66

77
use Fp\Functional\Option\None;
8+
use Fp\Functional\Option\Option;
89
use PHPUnit\Framework\TestCase;
910
use Tests\Mock\Baz;
1011
use Tests\Mock\Foo;
1112

13+
use Tests\Mock\IntEnum;
14+
use Tests\Mock\StringEnum;
15+
use function Fp\Cast\asEnumOf;
1216
use function Fp\Cast\asGenerator;
1317
use function Fp\Cast\asArray;
1418
use function Fp\Cast\asBool;
@@ -20,6 +24,7 @@
2024
use function Fp\Cast\asPairs;
2125
use function Fp\Cast\asPairsGenerator;
2226
use function Fp\Cast\asString;
27+
use function Fp\Cast\enumOf;
2328
use function Fp\Cast\fromPairs;
2429

2530
final class CastTest extends TestCase
@@ -31,6 +36,44 @@ public function testAsArray(): void
3136
$this->assertEquals($c, asArray($c));
3237
}
3338

39+
public function testAsEnumOf(): void
40+
{
41+
$this->assertEquals(Option::none(), asEnumOf(0, IntEnum::class));
42+
$this->assertEquals(Option::none(), asEnumOf('fst', IntEnum::class));
43+
$this->assertEquals(Option::none(), asEnumOf(0.42, IntEnum::class));
44+
$this->assertEquals(Option::none(), asEnumOf([], IntEnum::class));
45+
$this->assertEquals(Option::some(IntEnum::FST), asEnumOf(1, IntEnum::class));
46+
$this->assertEquals(Option::some(IntEnum::SND), asEnumOf(2, IntEnum::class));
47+
$this->assertEquals(Option::some(IntEnum::THR), asEnumOf(3, IntEnum::class));
48+
49+
$this->assertEquals(Option::none(), asEnumOf(0, StringEnum::class));
50+
$this->assertEquals(Option::none(), asEnumOf(0.42, StringEnum::class));
51+
$this->assertEquals(Option::none(), asEnumOf([], StringEnum::class));
52+
$this->assertEquals(Option::none(), asEnumOf('fth', StringEnum::class));
53+
$this->assertEquals(Option::some(StringEnum::FST), asEnumOf('fst', StringEnum::class));
54+
$this->assertEquals(Option::some(StringEnum::SND), asEnumOf('snd', StringEnum::class));
55+
$this->assertEquals(Option::some(StringEnum::THR), asEnumOf('thr', StringEnum::class));
56+
}
57+
58+
public function testEnumOf(): void
59+
{
60+
$this->assertEquals(Option::none(), enumOf(IntEnum::class)(0));
61+
$this->assertEquals(Option::none(), enumOf(IntEnum::class)('fst'));
62+
$this->assertEquals(Option::none(), enumOf(IntEnum::class)(0.42));
63+
$this->assertEquals(Option::none(), enumOf(IntEnum::class)([]));
64+
$this->assertEquals(Option::some(IntEnum::FST), enumOf(IntEnum::class)(1));
65+
$this->assertEquals(Option::some(IntEnum::SND), enumOf(IntEnum::class)(2));
66+
$this->assertEquals(Option::some(IntEnum::THR), enumOf(IntEnum::class)(3));
67+
68+
$this->assertEquals(Option::none(), enumOf(StringEnum::class)(0));
69+
$this->assertEquals(Option::none(), enumOf(StringEnum::class)(0.42));
70+
$this->assertEquals(Option::none(), enumOf(StringEnum::class)([]));
71+
$this->assertEquals(Option::none(), enumOf(StringEnum::class)('fth'));
72+
$this->assertEquals(Option::some(StringEnum::FST), enumOf(StringEnum::class)('fst'));
73+
$this->assertEquals(Option::some(StringEnum::SND), enumOf(StringEnum::class)('snd'));
74+
$this->assertEquals(Option::some(StringEnum::THR), enumOf(StringEnum::class)('thr'));
75+
}
76+
3477
public function testAsBool(): void
3578
{
3679
$this->assertTrue(asBool('true')->get());

0 commit comments

Comments
 (0)