Skip to content

Commit 02d470d

Browse files
committed
Improve DX experience
1 parent c1d1fe6 commit 02d470d

12 files changed

+181
-82
lines changed

src/Dictionary.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -165,28 +165,28 @@ public function keys(): array
165165
return array_keys($this->members);
166166
}
167167

168-
public function has(string|int ...$offsets): bool
168+
public function has(string|int ...$keys): bool
169169
{
170-
foreach ($offsets as $offset) {
171-
if (!is_string($offset) || !array_key_exists($offset, $this->members)) {
170+
foreach ($keys as $key) {
171+
if (!is_string($key) || !array_key_exists($key, $this->members)) {
172172
return false;
173173
}
174174
}
175175

176-
return [] !== $offsets;
176+
return [] !== $keys;
177177
}
178178

179179
/**
180180
* @throws SyntaxError If the key is invalid
181181
* @throws InvalidOffset If the key is not found
182182
*/
183-
public function get(string|int $offset): Value|InnerList
183+
public function get(string|int $key): Value|InnerList
184184
{
185-
if (!$this->has($offset)) {
186-
throw InvalidOffset::dueToKeyNotFound($offset);
185+
if (!$this->has($key)) {
186+
throw InvalidOffset::dueToKeyNotFound($key);
187187
}
188188

189-
return $this->members[$offset];
189+
return $this->members[$key];
190190
}
191191

192192
public function hasPair(int ...$indexes): bool

src/InnerList.php

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ public function hasMembers(): bool
134134
return [] !== $this->members;
135135
}
136136

137+
/**
138+
* @return array<int>
139+
*/
140+
public function keys(): array
141+
{
142+
return array_keys($this->members);
143+
}
144+
137145
/**
138146
* @return Iterator<int, Value>
139147
*/
@@ -142,15 +150,15 @@ public function getIterator(): Iterator
142150
yield from $this->members;
143151
}
144152

145-
public function has(string|int ...$offsets): bool
153+
public function has(string|int ...$keys): bool
146154
{
147-
foreach ($offsets as $offset) {
155+
foreach ($keys as $offset) {
148156
if (null === $this->filterIndex($offset)) {
149157
return false;
150158
}
151159
}
152160

153-
return [] !== $offsets;
161+
return [] !== $keys;
154162
}
155163

156164
private function filterIndex(string|int $index): int|null
@@ -170,11 +178,11 @@ private function filterIndex(string|int $index): int|null
170178
};
171179
}
172180

173-
public function get(string|int $offset): Value
181+
public function get(string|int $key): Value
174182
{
175-
$index = $this->filterIndex($offset);
183+
$index = $this->filterIndex($key);
176184
if (null === $index) {
177-
throw InvalidOffset::dueToIndexNotFound($offset);
185+
throw InvalidOffset::dueToIndexNotFound($key);
178186
}
179187

180188
return $this->members[$index];
@@ -209,12 +217,12 @@ public function push(StructuredField|Token|ByteSequence|DateTimeInterface|String
209217
*
210218
* @throws InvalidOffset If the index does not exist
211219
*/
212-
public function insert(int $index, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool ...$members): static
220+
public function insert(int $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool ...$members): static
213221
{
214-
$offset = $this->filterIndex($index);
222+
$offset = $this->filterIndex($key);
215223

216224
return match (true) {
217-
null === $offset => throw InvalidOffset::dueToIndexNotFound($index),
225+
null === $offset => throw InvalidOffset::dueToIndexNotFound($key),
218226
0 === $offset => $this->unshift(...$members),
219227
count($this->members) === $offset => $this->push(...$members),
220228
[] === $members => $this,
@@ -226,10 +234,10 @@ public function insert(int $index, StructuredField|Token|ByteSequence|DateTimeIn
226234
};
227235
}
228236

229-
public function replace(int $index, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
237+
public function replace(int $key, StructuredField|Token|ByteSequence|DateTimeInterface|Stringable|string|int|float|bool $member): static
230238
{
231-
if (null === ($offset = $this->filterIndex($index))) {
232-
throw InvalidOffset::dueToIndexNotFound($index);
239+
if (null === ($offset = $this->filterIndex($key))) {
240+
throw InvalidOffset::dueToIndexNotFound($key);
233241
}
234242

235243
$members = $this->members;
@@ -241,12 +249,12 @@ public function replace(int $index, StructuredField|Token|ByteSequence|DateTimeI
241249
/**
242250
* Deletes members associated with the list of instance indexes.
243251
*/
244-
public function remove(string|int ...$indexes): static
252+
public function remove(string|int ...$keys): static
245253
{
246254
$offsets = array_filter(
247255
array_map(
248256
fn (int $index): int|null => $this->filterIndex($index),
249-
array_filter($indexes, static fn (string|int $key): bool => is_int($key))
257+
array_filter($keys, static fn (string|int $key): bool => is_int($key))
250258
),
251259
fn (int|null $index): bool => null !== $index
252260
);

src/InnerListTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,4 +243,26 @@ public function it_forbids_adding_members_using_the_array_access_interface(): vo
243243

244244
InnerList::from('foobar', 'foobar', 'zero', 0)[0] = Item::from(false);
245245
}
246+
247+
248+
#[Test]
249+
public function it_can_returns_the_container_member_keys(): void
250+
{
251+
$instance = InnerList::from();
252+
253+
self::assertSame([], $instance->keys());
254+
255+
$newInstance = $instance
256+
->push(Item::from(false), Item::from(true));
257+
258+
self::assertSame([0, 1], $newInstance->keys());
259+
260+
$container = InnerList::from()
261+
->unshift('42')
262+
->push(42)
263+
->insert(1, 42.0)
264+
->replace(0, ByteSequence::fromDecoded('Hello World'));
265+
266+
self::assertSame([0, 1, 2], $container->keys());
267+
}
246268
}

src/Item.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,16 +117,22 @@ public static function fromPair(array $pair): self
117117
*/
118118
public static function from(mixed $value, iterable $parameters = []): self
119119
{
120-
return new self(match (true) {
120+
$parameters = Parameters::fromAssociative($parameters);
121+
if ($value instanceof Value) {
122+
$parameters = $value->parameters()->mergeAssociative($parameters);
123+
}
124+
125+
return new self(match (true) { /* @phpstan-ignore-line */
126+
$value instanceof Value => $value->value(),
127+
$value instanceof DateTimeInterface => self::filterDate($value),
121128
is_int($value) => self::filterIntegerRange($value, 'Integer'),
122129
is_float($value) => self::filterDecimal($value),
123130
is_string($value) || $value instanceof Stringable => self::filterString($value),
124-
$value instanceof DateTimeInterface => self::filterDate($value),
125131
is_bool($value),
126132
$value instanceof Token,
127133
$value instanceof ByteSequence => $value,
128134
default => throw new SyntaxError('The type "'.(is_object($value) ? $value::class : gettype($value)).'" is not supported.')
129-
}, Parameters::fromAssociative($parameters));
135+
}, $parameters);
130136
}
131137

132138
/**
@@ -184,13 +190,17 @@ private static function filterDate(DateTimeInterface $value): DateTimeImmutable
184190
return $value instanceof DateTimeImmutable ? $value : DateTimeImmutable::createFromInterface($value);
185191
}
186192

187-
public function value(): Token|ByteSequence|DateTimeImmutable|string|int|float|bool
193+
public function value(): ByteSequence|Token|DateTimeImmutable|string|int|float|bool
188194
{
189195
return $this->value;
190196
}
191197

192198
public function withValue(mixed $value): static
193199
{
200+
if ($value instanceof Value) {
201+
$value = $value->value();
202+
}
203+
194204
return match (true) {
195205
($this->value instanceof ByteSequence || $this->value instanceof Token) && $this->value->equals($value),
196206
$this->value instanceof DateTimeInterface && $value instanceof DateTimeInterface && $value == $this->value,

src/ItemTest.php

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,53 +43,79 @@ public function it_fails_to_instantiate_a_decimal_too_small(): void
4343
}
4444

4545
#[Test]
46-
public function it_instantiate_a_decimal(): void
46+
#[DataProvider('provideFrom1stArgument')]
47+
public function it_instantiate_many_types(mixed $value, string $expected): void
4748
{
48-
self::assertSame('42.0', Item::from(42.0)->toHttpValue());
49-
self::assertSame('42.0', (string) Item::from(42.0));
49+
self::assertSame($expected, Item::from($value)->toHttpValue());
5050
}
5151

5252
#[Test]
53-
public function it_fails_to_instantiate_a_integer_too_big(): void
53+
#[DataProvider('provideFrom1stArgument')]
54+
public function it_updates_item(mixed $value, string $expected): void
5455
{
55-
$this->expectException(SyntaxError::class);
56-
57-
Item::from(1_000_000_000_000_000);
56+
$parameters = Parameters::fromAssociative(['foo' => 'bar']);
57+
if ($value instanceof Value) {
58+
$expected = $value->withoutAllParameters()->toHttpValue();
59+
}
60+
61+
self::assertSame(
62+
$expected.$parameters->toHttpValue(),
63+
Item::from('hello-world', $parameters)->withValue($value)->toHttpValue()
64+
);
5865
}
5966

60-
#[Test]
61-
public function it_fails_to_instantiate_a_integer_too_small(): void
67+
/**
68+
* @return iterable<string, array{value:mixed, expected:string}>>
69+
*/
70+
public static function provideFrom1stArgument(): iterable
6271
{
63-
$this->expectException(SyntaxError::class);
72+
$item = Item::from(42, ['foobar' => 'baz']);
6473

65-
Item::from(-1_000_000_000_000_000);
74+
return [
75+
'decimal' => ['value' => 42.0, 'expected' => '42.0'],
76+
'string' => ['value' => 'forty-two', 'expected' => '"forty-two"'],
77+
'stringable' => ['value' => new class() implements Stringable {
78+
public function __toString(): string
79+
{
80+
return 'forty-two';
81+
}
82+
}, 'expected' => '"forty-two"'],
83+
'integer' => ['value' => 42, 'expected' => '42'],
84+
'boolean true' => ['value' => true, 'expected' => '?1'],
85+
'boolean false' => ['value' => false, 'expected' => '?0'],
86+
'token' => ['value' => Token::fromString('helloworld'), 'expected' => 'helloworld'],
87+
'byte sequence' => ['value' => ByteSequence::fromDecoded('foobar'), 'expected' => ':Zm9vYmFy:'],
88+
'datetime' => ['value' => new DateTime('2020-03-04 19:23:15'), 'expected' => '@1583349795'],
89+
'value' => ['value' => $item, 'expected' => $item->toHttpValue()],
90+
];
6691
}
6792

6893
#[Test]
69-
public function it_instantiates_an_integer(): void
94+
public function it_fails_to_instantiate_a_integer_too_big(): void
7095
{
71-
self::assertSame('42', Item::from(42)->toHttpValue());
96+
$this->expectException(SyntaxError::class);
97+
98+
Item::from(1_000_000_000_000_000);
7299
}
73100

74101
#[Test]
75-
public function it_instantiates_a_boolean(): void
102+
public function it_fails_to_instantiate_a_integer_too_small(): void
76103
{
77-
self::assertSame('?1', Item::from(true)->toHttpValue());
78-
self::assertSame('?0', Item::from(false)->toHttpValue());
104+
$this->expectException(SyntaxError::class);
105+
106+
Item::from(-1_000_000_000_000_000);
79107
}
80108

81109
#[Test]
82110
public function it_instantiates_a_token(): void
83111
{
84-
self::assertSame('helloworld', Item::from(Token::fromString('helloworld'))->toHttpValue());
85112
self::assertSame('helloworld', Item::fromToken('helloworld')->toHttpValue());
86113
}
87114

88115
#[Test]
89116
public function it_instantiates_a_date(): void
90117
{
91118
$item = Item::fromHttpValue('@1583349795');
92-
93119
self::assertEquals($item, Item::from(new DateTimeImmutable('2020-03-04 19:23:15')));
94120
self::assertEquals($item, Item::from(new DateTime('2020-03-04 19:23:15')));
95121
self::assertSame('@1583349795', $item->toHttpValue());

src/MemberContainer.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,28 @@ public function hasNoMembers(): bool;
2626
*/
2727
public function hasMembers(): bool;
2828

29+
/**
30+
* Returns an ordered list of the instance keys.
31+
*
32+
* @return array<TKey>
33+
*/
34+
public function keys(): array;
35+
2936
/**
3037
* @return TValue
3138
*/
32-
public function get(string|int $offset): StructuredField;
39+
public function get(string|int $key): StructuredField;
3340

3441
/**
3542
* Tells whether the instance contain a members at the specified offsets.
3643
*/
37-
public function has(string|int ...$offsets): bool;
44+
public function has(string|int ...$keys): bool;
3845

3946
/**
4047
* Deletes members associated with the list of submitted keys.
4148
*
4249
* This method MUST retain the state of the current instance, and return
4350
* an instance that contains the specified changes.
4451
*/
45-
public function remove(string|int ...$indexes): static;
52+
public function remove(string|int ...$keys): static;
4653
}

src/MemberList.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public function push(StructuredField ...$members): static;
3535
*
3636
* @throws InvalidOffset If the index does not exist
3737
*/
38-
public function insert(int $index, StructuredField ...$members): static;
38+
public function insert(int $key, StructuredField ...$members): static;
3939

4040
/**
4141
* Replaces the member associated with the index.
@@ -45,5 +45,5 @@ public function insert(int $index, StructuredField ...$members): static;
4545
*
4646
* @throws InvalidOffset If the index does not exist
4747
*/
48-
public function replace(int $index, StructuredField $member): static;
48+
public function replace(int $key, StructuredField $member): static;
4949
}

src/MemberOrderedMap.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,6 @@ public function hasPair(int ...$indexes): bool;
3535
*/
3636
public function pair(int $index): array;
3737

38-
/**
39-
* Returns an ordered list of the instance keys.
40-
*
41-
* @return array<TKey>
42-
*/
43-
public function keys(): array;
44-
4538
/**
4639
* Adds a member at the end of the instance otherwise updates the value associated with the key if already present.
4740
*

0 commit comments

Comments
 (0)