Skip to content

Commit 499847b

Browse files
Implement transformValidationSuccess (equivalent to map) for decoders (#24)
1 parent 9896d96 commit 499847b

File tree

17 files changed

+420
-75
lines changed

17 files changed

+420
-75
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
### Added
9+
- `trasformValidationSuccess` function for decoders. Structurally equivalent to a map. (#24)
810
### Fixed
9-
- Used Psalm specific annotations to avoid confusing IDEs without Psalm support. (#26)
11+
- Used Psalm specific annotations to avoid confusing IDEs without Psalm support. (#26)
12+
### Deprecated
13+
- Usage of codecs is deprecated in favour of decoders. (#24)
1014

1115
## [0.0.1] - 2021-04-30
1216
### Added

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ sh:
1111
docker-compose exec php bash
1212

1313
psalm:
14-
./vendor/bin/psalm --no-cache
14+
./vendor/bin/psalm src --no-cache
1515

1616
type-assertions:
1717
./vendor/bin/psalm tests/type-assertions --no-cache

src/Codecs.php

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
use Facile\PhpCodec\Internal\Primitives\BoolType;
1515
use Facile\PhpCodec\Internal\Primitives\CallableDecoder;
1616
use Facile\PhpCodec\Internal\Primitives\FloatType;
17-
use Facile\PhpCodec\Internal\Primitives\IntType;
17+
use Facile\PhpCodec\Internal\Primitives\IntDecoder;
1818
use Facile\PhpCodec\Internal\Primitives\MixedDecoder;
1919
use Facile\PhpCodec\Internal\Primitives\NullType;
2020
use Facile\PhpCodec\Internal\Primitives\StringType;
2121
use Facile\PhpCodec\Internal\Primitives\UndefinedDecoder;
2222
use Facile\PhpCodec\Internal\Useful\DateTimeFromIsoStringType;
23-
use Facile\PhpCodec\Internal\Useful\IntFromStringType;
23+
use Facile\PhpCodec\Internal\Useful\IntFromStringDecoder;
2424
use Facile\PhpCodec\Internal\Useful\RegexType;
25+
use Facile\PhpCodec\Utils\ConcreteCodec;
2526

2627
final class Codecs
2728
{
@@ -42,11 +43,15 @@ public static function string(): Codec
4243
}
4344

4445
/**
45-
* @psalm-return Codec<int, mixed, int>
46+
* @template I of mixed
47+
* @psalm-return Codec<int, I, int>
48+
*
49+
* @deprecated use decoder instead
50+
* @see Decoders::int()
4651
*/
4752
public static function int(): Codec
4853
{
49-
return new IntType();
54+
return self::fromDecoder(new IntDecoder());
5055
}
5156

5257
/**
@@ -79,10 +84,13 @@ public static function literal($x): Codec
7984

8085
/**
8186
* @psalm-return Codec<int, string, int>
87+
*
88+
* @deprecated use decoder instead
89+
* @see Decoders::intFromString()
8290
*/
8391
public static function intFromString(): Codec
8492
{
85-
return new IntFromStringType();
93+
return self::fromDecoder(new IntFromStringDecoder());
8694
}
8795

8896
/**
@@ -200,6 +208,9 @@ public static function fromDecoder(Decoder $decoder): Codec
200208
* @psalm-return Codec<U, mixed, U>
201209
*
202210
* @param null|mixed $default
211+
*
212+
* @deprecated use decoder instead
213+
* @see Decoders::undefined()
203214
*/
204215
public static function undefined($default = null): Codec
205216
{
@@ -209,6 +220,9 @@ public static function undefined($default = null): Codec
209220
/**
210221
* @psalm-template U of mixed
211222
* @psalm-return Codec<U, mixed, U>
223+
*
224+
* @deprecated use decoder instead
225+
* @see Decoders::mixed()
212226
*/
213227
public static function mixed(): Codec
214228
{
@@ -217,6 +231,9 @@ public static function mixed(): Codec
217231

218232
/**
219233
* @psalm-return Codec<callable, mixed, callable>
234+
*
235+
* @deprecated use decoder instead
236+
* @see Decoders::callable()
220237
*/
221238
public static function callable(): Codec
222239
{

src/Decoders.php

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,70 @@
44

55
namespace Facile\PhpCodec;
66

7+
use Facile\PhpCodec\Internal\Combinators\ComposeDecoder;
8+
use Facile\PhpCodec\Internal\Combinators\MapDecoder;
9+
use Facile\PhpCodec\Internal\Primitives\CallableDecoder;
10+
use Facile\PhpCodec\Internal\Primitives\IntDecoder;
11+
use Facile\PhpCodec\Internal\Primitives\MixedDecoder;
712
use Facile\PhpCodec\Internal\Primitives\UndefinedDecoder;
13+
use Facile\PhpCodec\Internal\Useful\IntFromStringDecoder;
14+
use Facile\PhpCodec\Utils\ConcreteDecoder;
15+
use Facile\PhpCodec\Validation\Context;
16+
use Facile\PhpCodec\Validation\Validation;
817

918
final class Decoders
1019
{
1120
private function __construct()
1221
{
1322
}
1423

24+
/**
25+
* @template I
26+
* @template A
27+
* @psalm-param callable(I, Context):Validation<A> $f
28+
* @psalm-param string $name
29+
* @psalm-return Decoder<I, A>
30+
*/
31+
public static function make(callable $f, string $name = 'anon'): Decoder
32+
{
33+
return new ConcreteDecoder($f, $name);
34+
}
35+
36+
/**
37+
* @template I
38+
* @template A
39+
* @template B
40+
* @psalm-param Decoder<A, B> $db
41+
* @psalm-param Decoder<I, A> $da
42+
* @psalm-return Decoder<I, B>
43+
*/
44+
public static function compose(Decoder $db, Decoder $da): Decoder
45+
{
46+
return new ComposeDecoder($db, $da);
47+
}
48+
49+
/**
50+
* This is structurally equivalent to a map function
51+
* map :: (a -> b) -> Decoder a -> Decoder b
52+
*
53+
* I still don't know if decoders could be functors or something more complicated.
54+
* By now, let me introduce it with this strange name. I just need this feature.
55+
*
56+
* @template I
57+
* @template A
58+
* @template B
59+
* @psalm-param callable(A):B $f
60+
* @psalm-param Decoder<I, A> $da
61+
* @psalm-return Decoder<I, B>
62+
*/
63+
public static function transformValidationSuccess(callable $f, Decoder $da): Decoder
64+
{
65+
return self::compose(
66+
new MapDecoder($f, $da->getName()),
67+
$da
68+
);
69+
}
70+
1571
/**
1672
* @psalm-template U
1773
* @psalm-param U $default
@@ -23,4 +79,36 @@ public static function undefined($default = null): Decoder
2379
{
2480
return new UndefinedDecoder($default);
2581
}
82+
83+
/**
84+
* @psalm-return Decoder<mixed, int>
85+
*/
86+
public static function int(): Decoder
87+
{
88+
return new IntDecoder();
89+
}
90+
91+
/**
92+
* @psalm-return Decoder<string, int>
93+
*/
94+
public static function intFromString(): Decoder
95+
{
96+
return new IntFromStringDecoder();
97+
}
98+
99+
/**
100+
* @psalm-return Decoder<mixed, mixed>
101+
*/
102+
public static function mixed(): Decoder
103+
{
104+
return new MixedDecoder();
105+
}
106+
107+
/**
108+
* @psalm-return Decoder<mixed, callable>
109+
*/
110+
public static function callable(): Decoder
111+
{
112+
return new CallableDecoder();
113+
}
26114
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Facile\PhpCodec\Internal\Combinators;
6+
7+
use Facile\PhpCodec\Decoder;
8+
use function Facile\PhpCodec\Internal\standardDecode;
9+
use Facile\PhpCodec\Validation\Context;
10+
use Facile\PhpCodec\Validation\Validation;
11+
12+
/**
13+
* @psalm-template IA
14+
* @psalm-template A
15+
* @psalm-template B
16+
*
17+
* @template-implements Decoder<IA, B>
18+
*/
19+
class ComposeDecoder implements Decoder
20+
{
21+
/** @var Decoder<A, B> */
22+
private $db;
23+
/** @var Decoder<IA, A> */
24+
private $da;
25+
26+
/**
27+
* @psalm-param Decoder<A, B> $db
28+
* @psalm-param Decoder<IA, A> $da
29+
*/
30+
public function __construct(
31+
Decoder $db,
32+
Decoder $da
33+
) {
34+
$this->db = $db;
35+
$this->da = $da;
36+
}
37+
38+
/**
39+
* @psalm-param IA $i
40+
* @psalm-param Context $context
41+
* @psalm-return Validation<B>
42+
*
43+
* @param mixed $i
44+
*/
45+
public function validate($i, Context $context): Validation
46+
{
47+
return Validation::bind(
48+
/**
49+
* @psalm-param A $aValue
50+
*
51+
* @param mixed $aValue
52+
*/
53+
function ($aValue) use ($context): Validation {
54+
return $this->db->validate($aValue, $context);
55+
},
56+
$this->da->validate($i, $context)
57+
);
58+
}
59+
60+
public function decode($i): Validation
61+
{
62+
return standardDecode($this, $i);
63+
}
64+
65+
public function getName(): string
66+
{
67+
return $this->db->getName();
68+
}
69+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Facile\PhpCodec\Internal\Combinators;
6+
7+
use Facile\PhpCodec\Decoder;
8+
use function Facile\PhpCodec\Internal\standardDecode;
9+
use Facile\PhpCodec\Validation\Context;
10+
use Facile\PhpCodec\Validation\Validation;
11+
12+
/**
13+
* @template A
14+
* @template B
15+
* @implements Decoder<A, B>
16+
*/
17+
class MapDecoder implements Decoder
18+
{
19+
/** @var callable(A):B */
20+
private $f;
21+
/** @var string */
22+
private $name;
23+
24+
/**
25+
* @psalm-param callable(A):B $f
26+
*
27+
* @param callable $f
28+
*/
29+
public function __construct(callable $f, string $name = 'map')
30+
{
31+
$this->f = $f;
32+
$this->name = $name;
33+
}
34+
35+
public function validate($i, Context $context): Validation
36+
{
37+
return Validation::success(($this->f)($i));
38+
}
39+
40+
public function decode($i): Validation
41+
{
42+
return standardDecode($this, $i);
43+
}
44+
45+
public function getName(): string
46+
{
47+
return $this->name;
48+
}
49+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Facile\PhpCodec\Internal\Primitives;
6+
7+
use Facile\PhpCodec\Decoder;
8+
use function Facile\PhpCodec\Internal\standardDecode;
9+
use Facile\PhpCodec\Validation\Context;
10+
use Facile\PhpCodec\Validation\Validation;
11+
12+
/**
13+
* @template I of mixed
14+
* @implements Decoder<I, int>
15+
*/
16+
class IntDecoder implements Decoder
17+
{
18+
public function validate($i, Context $context): Validation
19+
{
20+
return \is_int($i)
21+
? Validation::success($i)
22+
: Validation::failure($i, $context);
23+
}
24+
25+
public function decode($i): Validation
26+
{
27+
return standardDecode($this, $i);
28+
}
29+
30+
public function getName(): string
31+
{
32+
return 'int';
33+
}
34+
}

src/Internal/Primitives/IntRefiner.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
/**
1010
* @implements Refiner<int>
11+
*
12+
* @deprecated
1113
*/
1214
class IntRefiner implements Refiner
1315
{

0 commit comments

Comments
 (0)