Lines 86.20% 25 / 29
Methods 50.00% 3 / 6
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 handledClass 100.00% 1 / 1 100.00% 1 / 1 1
 keySuffix 100.00% 1 / 1 100.00% 1 / 1 1
 encodeValue 100.00% 6 / 6 100.00% 1 / 1 2
 decodeValue 85.71% 6 / 7 0.00% 0 / 1 2.01
 decodeContext 75.00% 6 / 8 0.00% 0 / 1 7.77
 decodeScalarList 83.33% 5 / 6 0.00% 0 / 1 4.07
14final readonly class ExceptionDetailsConverter implements ValueConverter
15{
16    public function handledClass(): string
17    {
18        return ExceptionDetails::class;
19    }
20
21    public function keySuffix(): string
22    {
23        return '';
24    }
25
26    /** @return array{class: string, reason: string, context: object|null} */
27    public function encodeValue(object $value): array
28    {
29        \assert($value instanceof ExceptionDetails);
30
31        return [
32            'class' => $value->class,
33            'reason' => $value->reason,
34            'context' => $value->context === null ? null : (object) $value->context,
35        ];
36    }
37
38    /** @throws JsonShapeException */
39    public function decodeValue(mixed $value, string $path): ExceptionDetails
40    {
41        if (!\is_array($value)) {
42            throw JsonShapeException::wrongType($path, 'an object', get_debug_type($value));
43        }
44
45        return new ExceptionDetails(
46            class: JsonShape::requireString($value, 'class'),
47            reason: JsonShape::requireString($value, 'reason'),
48            context: $this->decodeContext($value, $path . '.context'),
49        );
50    }
51
52    /**
53     * @param array<array-key, mixed> $details
54     * @return ExceptionContext|null
55     * @throws JsonShapeException
56     */
57    private function decodeContext(array $details, string $path): ?array
58    {
59        if (($details['context'] ?? null) === null) {
60            return null;
61        }
62
63        $context = [];
64
65        foreach (JsonShape::requireObject($details, 'context') as $name => $value) {
66            $context[$name] = match (true) {
67                $value === null, \is_scalar($value) => $value,
68                \is_array($value) && array_is_list($value) => $this->decodeScalarList($path . '.' . $name, $value),
69                default => throw JsonShapeException::wrongType($path . '.' . $name, 'a scalar, null or a list of them', get_debug_type($value)),
70            };
71        }
72
73        return $context;
74    }
75
76    /**
77     * @param list<mixed> $values
78     * @return list<scalar|null>
79     * @throws JsonShapeException
80     */
81    private function decodeScalarList(string $path, array $values): array
82    {
83        $scalars = [];
84
85        foreach ($values as $value) {
86            $scalars[] = $value === null || \is_scalar($value)
87                ? $value
88                : throw JsonShapeException::wrongType($path, 'a list of scalars', get_debug_type($value));
89        }
90
91        return $scalars;
92    }
93}