Lines 100.00% 26 / 26
Functions and Methods 100.00% 13 / 13
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
ScopedStore 100.00% 26 / 26 100.00% 13 / 13 21 100.00% 1 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 has 100.00% 1 / 1 100.00% 1 / 1 1
 getInt 100.00% 2 / 2 100.00% 1 / 1 2
 getFloat 100.00% 2 / 2 100.00% 1 / 1 2
 getString 100.00% 2 / 2 100.00% 1 / 1 2
 getBool 100.00% 2 / 2 100.00% 1 / 1 2
 getObject 100.00% 2 / 2 100.00% 1 / 1 2
 listKeys 100.00% 3 / 3 100.00% 1 / 1 1
 set 100.00% 2 / 2 100.00% 1 / 1 1
 increment 100.00% 6 / 6 100.00% 1 / 1 4
 delete 100.00% 1 / 1 100.00% 1 / 1 1
 clear 100.00% 1 / 1 100.00% 1 / 1 1
 readRawValue 100.00% 1 / 1 100.00% 1 / 1 1
1<?php
2
3declare(strict_types=1);
4
5namespace Stewart\Store;
6
7use Stewart\Contracts\Exception\StoreError;
8use Stewart\Contracts\Exception\StoreException;
9use Stewart\Contracts\Store\Storable;
10use Stewart\Contracts\Store\Store;
11use Stewart\Contracts\Time\Duration;
12
13final readonly class ScopedStore implements Store
14{
15    public function __construct(
16        private StoreBackend $backend,
17        private Keyspace $keyspace,
18        private StoreValueCodec $codec,
19    ) {}
20
21    public function has(string $key): bool
22    {
23        return $this->backend->exists($this->keyspace->buildFullKey($key));
24    }
25
26    public function getInt(string $key): ?int
27    {
28        $raw = $this->readRawValue($key);
29
30        return $raw === null ? null : $this->codec->decodeInt($key, $raw);
31    }
32
33    public function getFloat(string $key): ?float
34    {
35        $raw = $this->readRawValue($key);
36
37        return $raw === null ? null : $this->codec->decodeFloat($key, $raw);
38    }
39
40    public function getString(string $key): ?string
41    {
42        $raw = $this->readRawValue($key);
43
44        return $raw === null ? null : $this->codec->decodeString($key, $raw);
45    }
46
47    public function getBool(string $key): ?bool
48    {
49        $raw = $this->readRawValue($key);
50
51        return $raw === null ? null : $this->codec->decodeBool($key, $raw);
52    }
53
54    /**
55     * @template T of Storable
56     *
57     * @param class-string<T> $class
58     * @return T|null
59     */
60    public function getObject(string $key, string $class): ?Storable
61    {
62        $raw = $this->readRawValue($key);
63
64        return $raw === null ? null : $this->codec->decodeObject($key, $raw, $class);
65    }
66
67    public function listKeys(): array
68    {
69        $keys = array_map($this->keyspace->extractRelativeKey(...), $this->backend->keysWithPrefix($this->keyspace->prefix));
70        sort($keys);
71
72        return $keys;
73    }
74
75    public function set(string $key, bool|int|float|string|Storable $value, ?Duration $ttl = null): void
76    {
77        $ttl?->requireAtLeastOneMillisecond('a store TTL');
78        $this->backend->write($this->keyspace->buildFullKey($key), $this->codec->encodeValue($key, $value), $ttl);
79    }
80
81    public function increment(string $key, int $by = 1): int
82    {
83        try {
84            return $this->backend->increment($this->keyspace->buildFullKey($key), $by);
85        } catch (StoreException $e) {
86            $detail = $e->findDetail();
87
88            throw $e->reason === StoreError::ValueNotIncrementable && $detail !== null
89                ? StoreException::createForReason(StoreError::NotIncrementable, $e, key: $key, detail: $detail)
90                : $e;
91        }
92    }
93
94    public function delete(string $key): void
95    {
96        $this->backend->remove($this->keyspace->buildFullKey($key));
97    }
98
99    public function clear(): void
100    {
101        $this->backend->removeByPrefix($this->keyspace->prefix);
102    }
103
104    private function readRawValue(string $key): ?string
105    {
106        return $this->backend->read($this->keyspace->buildFullKey($key));
107    }
108}