Skip to content

Commit dcb4bc9

Browse files
committed
add mechanism to lookup first/last value matching a predicate while transforming the value
1 parent e1161f1 commit dcb4bc9

13 files changed

Lines changed: 835 additions & 0 deletions

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66

77
- `Innmind\Immutable\Predicate::and()`
88
- `Innmind\Immutable\Predicate::or()`
9+
- `Innmind\Immutable\Sequence->lookup()->first()->maybe()`
10+
- `Innmind\Immutable\Sequence->lookup()->first()->attempt()`
11+
- `Innmind\Immutable\Sequence->lookup()->first()->either()`
12+
- `Innmind\Immutable\Sequence->lookup()->last()->maybe()`
13+
- `Innmind\Immutable\Sequence->lookup()->last()->attempt()`
14+
- `Innmind\Immutable\Sequence->lookup()->last()->either()`
915

1016
### Changed
1117

docs/structures/sequence.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,78 @@ $firstOdd; // Maybe::just(9)
164164
$sequence->find(static fn() => false); // Maybe::nothing()
165165
```
166166

167+
### `->lookup()`
168+
169+
This is similar to `->find()` except it allows to return a computed value while searching for it. This is useful when parsing content.
170+
171+
=== "`->maybe()`"
172+
```php
173+
final class Email
174+
{
175+
/**
176+
* @return Maybe<self>
177+
*/
178+
public static function of(string $email): Maybe
179+
{
180+
}
181+
}
182+
183+
$email = Sequence::of('foo', 'valid-email@example.com', 'invalid')
184+
->lookup()
185+
->first()
186+
->maybe(Email::of(...));
187+
$email == Maybe::just(new Email('valid-email@example.com')); // true
188+
```
189+
190+
=== "`->attempt()`"
191+
```php
192+
final class Email
193+
{
194+
/**
195+
* @return Attempt<self>
196+
*/
197+
public static function of(string $email): Attempt
198+
{
199+
}
200+
}
201+
202+
$email = Sequence::of('foo', 'valid-email@example.com', 'invalid')
203+
->lookup()
204+
->first()
205+
->attempt(
206+
new \RuntimeException('No valid email found'),
207+
Email::of(...),
208+
);
209+
$email == Attempt::result(new Email('valid-email@example.com')); // true
210+
```
211+
212+
=== "`->either()`"
213+
```php
214+
final class Email
215+
{
216+
/**
217+
* @return Either<string, self>
218+
*/
219+
public static function of(string $email): Either
220+
{
221+
}
222+
}
223+
224+
$email = Sequence::of('foo', 'valid-email@example.com', 'invalid')
225+
->lookup()
226+
->first()
227+
->attempt(
228+
'No valid email found', #(1)
229+
Email::of(...),
230+
);
231+
$email == Either::right(new Email('valid-email@example.com')); // true
232+
```
233+
234+
1. Must be of the same type as the left side returned by `Email::of()`
235+
236+
!!! note ""
237+
If you need to access the last value that matches the predicate you can use `#!php ->lookup()->last()` instead of `#!php ->lookup()->first()`.
238+
167239
### `->matches()` :material-memory-arrow-down:
168240

169241
Check if all the elements of the sequence matches the given predicate.

properties/Sequence.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ public static function list(): array
3434
{
3535
return [
3636
Sequence\Windows::class,
37+
Sequence\LookupFirstMaybe::class,
38+
Sequence\LookupFirstAttempt::class,
39+
Sequence\LookupFirstEither::class,
40+
Sequence\LookupLastMaybe::class,
41+
Sequence\LookupLastAttempt::class,
42+
Sequence\LookupLastEither::class,
3743
];
3844
}
3945
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
namespace Properties\Innmind\Immutable\Sequence;
5+
6+
use Innmind\Immutable\{
7+
Sequence,
8+
Attempt,
9+
};
10+
use Innmind\BlackBox\{
11+
Set,
12+
Property,
13+
Runner\Assert,
14+
};
15+
16+
/**
17+
* @implements Property<Sequence>
18+
*/
19+
final class LookupFirstAttempt implements Property
20+
{
21+
private function __construct(
22+
private Sequence $prefix,
23+
private mixed $a,
24+
private mixed $b,
25+
) {
26+
}
27+
28+
public static function any(): Set\Provider
29+
{
30+
return Set::compose(
31+
static fn(...$args) => new self(...$args),
32+
Set::sequence(Set::type())->map(static fn($values) => Sequence::of(...$values)),
33+
Set::type(),
34+
Set::type(),
35+
);
36+
}
37+
38+
public function applicableTo(object $systemUnderTest): bool
39+
{
40+
return true;
41+
}
42+
43+
public function ensureHeldBy(Assert $assert, object $systemUnderTest): object
44+
{
45+
$default = new \Exception;
46+
47+
$assert->same(
48+
$default,
49+
$systemUnderTest
50+
->lookup()
51+
->first()
52+
->attempt(
53+
$default,
54+
static fn() => Attempt::error($default),
55+
)
56+
->match(
57+
static fn() => null,
58+
static fn($error) => $error,
59+
),
60+
);
61+
$assert->same(
62+
$this->b,
63+
$systemUnderTest
64+
->prepend($this->prefix->add($this->a))
65+
->lookup()
66+
->first()
67+
->attempt(
68+
$default,
69+
fn($value) => match ($value) {
70+
$this->a => Attempt::result($this->b),
71+
default => Attempt::error($default),
72+
},
73+
)
74+
->match(
75+
static fn($value) => $value,
76+
static fn() => null,
77+
),
78+
);
79+
80+
return $systemUnderTest;
81+
}
82+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
namespace Properties\Innmind\Immutable\Sequence;
5+
6+
use Innmind\Immutable\{
7+
Sequence,
8+
Either,
9+
};
10+
use Innmind\BlackBox\{
11+
Set,
12+
Property,
13+
Runner\Assert,
14+
};
15+
16+
/**
17+
* @implements Property<Sequence>
18+
*/
19+
final class LookupFirstEither implements Property
20+
{
21+
private function __construct(
22+
private Sequence $prefix,
23+
private mixed $a,
24+
private mixed $b,
25+
private mixed $left,
26+
) {
27+
}
28+
29+
public static function any(): Set\Provider
30+
{
31+
return Set::compose(
32+
static fn(...$args) => new self(...$args),
33+
Set::sequence(Set::type())->map(static fn($values) => Sequence::of(...$values)),
34+
Set::type(),
35+
Set::type(),
36+
Set::type(),
37+
);
38+
}
39+
40+
public function applicableTo(object $systemUnderTest): bool
41+
{
42+
return true;
43+
}
44+
45+
public function ensureHeldBy(Assert $assert, object $systemUnderTest): object
46+
{
47+
$assert->same(
48+
$this->left,
49+
$systemUnderTest
50+
->lookup()
51+
->first()
52+
->either(
53+
$this->left,
54+
fn() => Either::left($this->left),
55+
)
56+
->match(
57+
static fn() => null,
58+
static fn($left) => $left,
59+
),
60+
);
61+
$assert->same(
62+
$this->b,
63+
$systemUnderTest
64+
->prepend($this->prefix->add($this->a))
65+
->lookup()
66+
->first()
67+
->either(
68+
$this->left,
69+
fn($value) => match ($value) {
70+
$this->a => Either::right($this->b),
71+
default => Either::left($this->left),
72+
},
73+
)
74+
->match(
75+
static fn($value) => $value,
76+
static fn() => null,
77+
),
78+
);
79+
80+
return $systemUnderTest;
81+
}
82+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
namespace Properties\Innmind\Immutable\Sequence;
5+
6+
use Innmind\Immutable\{
7+
Sequence,
8+
Maybe,
9+
};
10+
use Innmind\BlackBox\{
11+
Set,
12+
Property,
13+
Runner\Assert,
14+
};
15+
16+
/**
17+
* @implements Property<Sequence>
18+
*/
19+
final class LookupFirstMaybe implements Property
20+
{
21+
private function __construct(
22+
private Sequence $prefix,
23+
private mixed $a,
24+
private mixed $b,
25+
) {
26+
}
27+
28+
public static function any(): Set\Provider
29+
{
30+
return Set::compose(
31+
static fn(...$args) => new self(...$args),
32+
Set::sequence(Set::type())->map(static fn($values) => Sequence::of(...$values)),
33+
Set::type(),
34+
Set::type(),
35+
);
36+
}
37+
38+
public function applicableTo(object $systemUnderTest): bool
39+
{
40+
return true;
41+
}
42+
43+
public function ensureHeldBy(Assert $assert, object $systemUnderTest): object
44+
{
45+
$assert->null(
46+
$systemUnderTest
47+
->lookup()
48+
->first()
49+
->maybe(Maybe::nothing(...))
50+
->match(
51+
static fn($value) => $value,
52+
static fn() => null,
53+
),
54+
);
55+
$assert->same(
56+
$this->b,
57+
$systemUnderTest
58+
->prepend($this->prefix->add($this->a))
59+
->lookup()
60+
->first()
61+
->maybe(fn($value) => match ($value) {
62+
$this->a => Maybe::just($this->b),
63+
default => Maybe::nothing(),
64+
})
65+
->match(
66+
static fn($value) => $value,
67+
static fn() => null,
68+
),
69+
);
70+
71+
return $systemUnderTest;
72+
}
73+
}

0 commit comments

Comments
 (0)