Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypedMap #1

Open
wants to merge 1 commit into
base: 0.1.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions src/MutableTypedMap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php

declare(strict_types=1);

namespace Typhoon\DataStructures;

/**
* @api
*/
final class MutableTypedMap extends TypedMap
{
/**
* @param array<non-negative-int, mixed> $values
*/
private function __construct(

Check warning on line 15 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L15

Added line #L15 was not covered by tests
private array $values = [],
) {}

Check warning on line 17 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L17

Added line #L17 was not covered by tests

public static function create(TypedKVPair ...$kvPairs): self

Check warning on line 19 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L19

Added line #L19 was not covered by tests
{
$map = new self();

Check warning on line 21 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L21

Added line #L21 was not covered by tests

foreach ($kvPairs as $pair) {
$map->values[$pair->key->index] = $pair->value;

Check warning on line 24 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L23-L24

Added lines #L23 - L24 were not covered by tests
}

return $map;

Check warning on line 27 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L27

Added line #L27 was not covered by tests
}

public function with(TypedKey $key, mixed $value): static

Check warning on line 30 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L30

Added line #L30 was not covered by tests
{
$values = $this->values;
$values[$key->index] = $value;

Check warning on line 33 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L32-L33

Added lines #L32 - L33 were not covered by tests

return new self($values);

Check warning on line 35 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L35

Added line #L35 was not covered by tests
}

public function withAll(self $map): static

Check warning on line 38 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L38

Added line #L38 was not covered by tests
{
return new self(array_replace($this->values, $map->values));

Check warning on line 40 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L40

Added line #L40 was not covered by tests
}

public function without(TypedKey ...$keys): static

Check warning on line 43 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L43

Added line #L43 was not covered by tests
{
$values = $this->values;

Check warning on line 45 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L45

Added line #L45 was not covered by tests

foreach ($keys as $key) {
unset($values[$key->index]);

Check warning on line 48 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L47-L48

Added lines #L47 - L48 were not covered by tests
}

return new self($values);

Check warning on line 51 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L51

Added line #L51 was not covered by tests
}

public function contains(TypedKey $key): bool

Check warning on line 54 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L54

Added line #L54 was not covered by tests
{
return \array_key_exists($key->index, $this->values);

Check warning on line 56 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L56

Added line #L56 was not covered by tests
}

public function offsetExists(mixed $offset): bool

Check warning on line 59 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L59

Added line #L59 was not covered by tests
{
return \array_key_exists($offset->index, $this->values);

Check warning on line 61 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L61

Added line #L61 was not covered by tests
}

/**
* @template V
* @param TypedKey<V> $key
* @return V
*/
public function get(TypedKey $key): mixed

Check warning on line 69 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L69

Added line #L69 was not covered by tests
{
if (\array_key_exists($key->index, $this->values)) {

Check warning on line 71 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L71

Added line #L71 was not covered by tests
/** @var V */
return $this->values[$key->index];

Check warning on line 73 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L73

Added line #L73 was not covered by tests
}

return $key->default($this);

Check warning on line 76 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L76

Added line #L76 was not covered by tests
}

/**
* @template V
* @param TypedKey<V> $offset
* @return V
*/
public function offsetGet(mixed $offset): mixed

Check warning on line 84 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L84

Added line #L84 was not covered by tests
{
if (\array_key_exists($offset->index, $this->values)) {

Check warning on line 86 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L86

Added line #L86 was not covered by tests
/** @var V */
return $this->values[$offset->index];

Check warning on line 88 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L88

Added line #L88 was not covered by tests
}

return $offset->default($this);

Check warning on line 91 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L91

Added line #L91 was not covered by tests
}

public function count(): int

Check warning on line 94 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L94

Added line #L94 was not covered by tests
{
return \count($this->values);

Check warning on line 96 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L96

Added line #L96 was not covered by tests
}

/**
* @template V
* @param TypedKey<V> $key
* @param V $value
*/
public function put(TypedKey $key, mixed $value): void

Check warning on line 104 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L104

Added line #L104 was not covered by tests
{
$this->values[$key->index] = $value;

Check warning on line 106 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L106

Added line #L106 was not covered by tests
}

public function putAll(self $map): void

Check warning on line 109 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L109

Added line #L109 was not covered by tests
{
$this->values = array_replace($this->values, $map->values);

Check warning on line 111 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L111

Added line #L111 was not covered by tests
}

public function remove(TypedKey ...$keys): void

Check warning on line 114 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L114

Added line #L114 was not covered by tests
{
foreach ($keys as $key) {
unset($this->values[$key->index]);

Check warning on line 117 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L116-L117

Added lines #L116 - L117 were not covered by tests
}
}

/**
* @return list<array{class-string<TypedKey>, non-empty-string, mixed}>
*/
public function __serialize(): array

Check warning on line 124 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L124

Added line #L124 was not covered by tests
{
return array_map(
static fn(TypedKey $key, mixed $value): array => [$key::class, $key->method, $value],
TypedKey::byIndexes(array_keys($this->values)),
$this->values,

Check warning on line 129 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L126-L129

Added lines #L126 - L129 were not covered by tests
);
}

/**
* @param list<array{class-string<TypedKey>, non-empty-string, mixed}> $data
*/
public function __unserialize(array $data): void

Check warning on line 136 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L136

Added line #L136 was not covered by tests
{
foreach ($data as [$class, $method, $value]) {
$key = $class::$method();
\assert($key instanceof $class);
$this->values[$key->index] = $value;

Check warning on line 141 in src/MutableTypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/MutableTypedMap.php#L138-L141

Added lines #L138 - L141 were not covered by tests
}
}
}
21 changes: 21 additions & 0 deletions src/TypedKVPair.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Typhoon\DataStructures;

/**
* @api
* @template V
*/
final class TypedKVPair
{
/**
* @param TypedKey<V> $key
* @param V $value
*/
public function __construct(

Check warning on line 17 in src/TypedKVPair.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKVPair.php#L17

Added line #L17 was not covered by tests
public readonly TypedKey $key,
public readonly mixed $value,
) {}

Check warning on line 20 in src/TypedKVPair.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKVPair.php#L20

Added line #L20 was not covered by tests
}
101 changes: 101 additions & 0 deletions src/TypedKey.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

declare(strict_types=1);

namespace Typhoon\DataStructures;

/**
* @api
* @template V
* @psalm-consistent-templates
*/
abstract class TypedKey
{
/**
* @var array<non-empty-string, self>
*/
private static array $keys = [];

/**
* @internal
* @psalm-internal Typhoon\DataStructures
* @param list<non-negative-int> $indexes
* @return list<self>
*/
final public static function byIndexes(array $indexes): array

Check warning on line 25 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L25

Added line #L25 was not covered by tests
{
$keys = array_values(self::$keys);

Check warning on line 27 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L27

Added line #L27 was not covered by tests

return array_map(
static fn(int $index): self => $keys[$index] ?? throw new \LogicException(),
$indexes,

Check warning on line 31 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L29-L31

Added lines #L29 - L31 were not covered by tests
);
}

/**
* @template D
* @param ?callable(TypedMap): D $default
* @return static<D>
*/
final protected static function init(?callable $default = null): static

Check warning on line 40 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L40

Added line #L40 was not covered by tests
{
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1] ?? [];

Check warning on line 42 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L42

Added line #L42 was not covered by tests

\assert(
isset($trace['class'], $trace['function']) && $trace['class'] === static::class && $trace['function'] !== '',
\sprintf('Invalid %s call', self::class),

Check warning on line 46 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L44-L46

Added lines #L44 - L46 were not covered by tests
);

$name = \sprintf('%s::%s', static::class, $trace['function']);

Check warning on line 49 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L49

Added line #L49 was not covered by tests

\assert(!isset(self::$keys[$name]), \sprintf('Please ensure you memoize key in %s()', $name));

Check warning on line 51 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L51

Added line #L51 was not covered by tests

return self::$keys[$name] = new static(
index: \count(self::$keys),
method: $trace['function'],
default: $default ?? static fn(): never => throw new \LogicException(\sprintf('Key %s() does not have a default value', $name)),

Check warning on line 56 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L53-L56

Added lines #L53 - L56 were not covered by tests
);
}

/**
* @param non-negative-int $index
* @param non-empty-string $method
* @param callable(TypedMap): V $default
*/
final private function __construct(

Check warning on line 65 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L65

Added line #L65 was not covered by tests
public readonly int $index,
public readonly string $method,
private readonly mixed $default,
) {}

Check warning on line 69 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L69

Added line #L69 was not covered by tests

/**
* @return V
*/
final public function default(TypedMap $map): mixed

Check warning on line 74 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L74

Added line #L74 was not covered by tests
{
return ($this->default)($map);

Check warning on line 76 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L76

Added line #L76 was not covered by tests
}

/**
* @return non-empty-string
*/
final public function toString(): string

Check warning on line 82 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L82

Added line #L82 was not covered by tests
{
return self::class . '::' . $this->method . '()';

Check warning on line 84 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L84

Added line #L84 was not covered by tests
}

final public function __serialize(): never

Check warning on line 87 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L87

Added line #L87 was not covered by tests
{
throw new \BadMethodCallException(\sprintf('%s does not support serialization', self::class));

Check warning on line 89 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L89

Added line #L89 was not covered by tests
}

final public function __unserialize(array $_data): never

Check warning on line 92 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L92

Added line #L92 was not covered by tests
{
throw new \BadMethodCallException(\sprintf('%s does not support deserialization', self::class));

Check warning on line 94 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L94

Added line #L94 was not covered by tests
}

final public function __clone()

Check warning on line 97 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L97

Added line #L97 was not covered by tests
{
throw new \BadMethodCallException(\sprintf('%s does not support cloning', self::class));

Check warning on line 99 in src/TypedKey.php

View check run for this annotation

Codecov / codecov/patch

src/TypedKey.php#L99

Added line #L99 was not covered by tests
}
}
62 changes: 62 additions & 0 deletions src/TypedMap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

namespace Typhoon\DataStructures;

/**
* @api
* @implements \ArrayAccess<TypedKey, mixed>
* @psalm-suppress UnusedClass
*/
abstract class TypedMap implements \ArrayAccess, \Countable
{
public static function create(TypedKVPair ...$kvPairs): self

Check warning on line 14 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L14

Added line #L14 was not covered by tests
{
return MutableTypedMap::create(...$kvPairs);

Check warning on line 16 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L16

Added line #L16 was not covered by tests
}

/**
* @template V
* @param TypedKey<V> $key
* @param V $value
*/
abstract public function with(TypedKey $key, mixed $value): static;

Check warning on line 24 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L24

Added line #L24 was not covered by tests

abstract public function withAll(MutableTypedMap $map): static;

Check warning on line 26 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L26

Added line #L26 was not covered by tests

abstract public function without(TypedKey ...$keys): static;

Check warning on line 28 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L28

Added line #L28 was not covered by tests

abstract public function contains(TypedKey $key): bool;

Check warning on line 30 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L30

Added line #L30 was not covered by tests

abstract public function offsetExists(mixed $offset): bool;

Check warning on line 32 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L32

Added line #L32 was not covered by tests

/**
* @template V
* @param TypedKey<V> $key
* @return V
*/
abstract public function get(TypedKey $key): mixed;

Check warning on line 39 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L39

Added line #L39 was not covered by tests

/**
* @template V
* @param TypedKey<V> $offset
* @return V
*/
abstract public function offsetGet(mixed $offset): mixed;

Check warning on line 46 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L46

Added line #L46 was not covered by tests

/**
* @return non-negative-int
*/
abstract public function count(): int;

Check warning on line 51 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L51

Added line #L51 was not covered by tests

public function offsetSet(mixed $offset, mixed $value): never

Check warning on line 53 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L53

Added line #L53 was not covered by tests
{
throw new \BadMethodCallException();

Check warning on line 55 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L55

Added line #L55 was not covered by tests
}

public function offsetUnset(mixed $offset): never

Check warning on line 58 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L58

Added line #L58 was not covered by tests
{
throw new \BadMethodCallException();

Check warning on line 60 in src/TypedMap.php

View check run for this annotation

Codecov / codecov/patch

src/TypedMap.php#L60

Added line #L60 was not covered by tests
}
}
Loading