4
4
5
5
namespace Bakame \Http \StructuredFields ;
6
6
7
+ use DateTimeImmutable ;
8
+ use ValueError ;
9
+
7
10
/**
8
11
* @phpstan-type RecordData array{
9
12
* name: string,
10
13
* header_type: 'dictionary'|'list'|'item',
11
14
* raw: array<string>,
12
15
* canonical?: array<string>,
13
16
* must_fail?: bool,
14
- * can_fail?: bool
17
+ * can_fail?: bool,
18
+ * expected?: array,
15
19
* }
20
+ * @phpstan-type itemValue array{__type:string, value:int|string}|string|bool|int|float|null
16
21
*/
17
22
final class Record
18
23
{
@@ -26,6 +31,7 @@ private function __construct(
26
31
public readonly array $ canonical ,
27
32
public readonly bool $ mustFail ,
28
33
public readonly bool $ canFail ,
34
+ public readonly ?StructuredField $ expected
29
35
) {
30
36
}
31
37
@@ -34,7 +40,7 @@ private function __construct(
34
40
*/
35
41
public static function fromDecoded (array $ data ): self
36
42
{
37
- $ data += ['canonical ' => $ data ['raw ' ], 'must_fail ' => false , 'can_fail ' => false ];
43
+ $ data += ['canonical ' => $ data ['raw ' ], 'must_fail ' => false , 'can_fail ' => false , ' expected ' => [] ];
38
44
39
45
return new self (
40
46
$ data ['name ' ],
@@ -43,6 +49,89 @@ public static function fromDecoded(array $data): self
43
49
$ data ['canonical ' ],
44
50
$ data ['must_fail ' ],
45
51
$ data ['can_fail ' ],
52
+ self ::parseExpected ($ data ['header_type ' ], $ data ['expected ' ])
46
53
);
47
54
}
55
+
56
+ private static function parseExpected (string $ dataTypeValue , array $ expected ): ?StructuredField
57
+ {
58
+ return match (DataType::tryFrom ($ dataTypeValue )) {
59
+ DataType::Dictionary => self ::parseDictionary ($ expected ),
60
+ DataType::List => self ::parseList ($ expected ),
61
+ DataType::Item => self ::parseItem ($ expected ),
62
+ default => null ,
63
+ };
64
+ }
65
+
66
+ /**
67
+ * @param itemValue $data
68
+ */
69
+ private static function parseValue (array |string |bool |int |float |null $ data ): Token |DateTimeImmutable |ByteSequence |DisplayString |string |bool |int |float |null
70
+ {
71
+ return match (true ) {
72
+ !is_array ($ data ) => $ data ,
73
+ 2 !== count ($ data ),
74
+ !isset ($ data ['__type ' ], $ data ['value ' ]) => throw new ValueError ('Unknown or unsupported type: ' .json_encode ($ data )),
75
+ default => match (Type::tryFrom ($ data ['__type ' ])) {
76
+ Type::Token => Token::fromString ($ data ['value ' ]),
77
+ Type::Date => (new DateTimeImmutable ())->setTimestamp ((int ) $ data ['value ' ]),
78
+ Type::DisplayString => DisplayString::fromDecoded ($ data ['value ' ]),
79
+ Type::ByteSequence => ByteSequence::fromDecoded (base32_decode (encoded: $ data ['value ' ], strict: true )),
80
+ default => throw new ValueError ('Unknown or unsupported type: ' .json_encode ($ data )),
81
+ },
82
+ };
83
+ }
84
+
85
+ /**
86
+ * @param array<array{0:string, 1:itemValue> $parameters
87
+ */
88
+ private static function parseParameters (array $ parameters ): Parameters
89
+ {
90
+ return Parameters::fromPairs (array_map (
91
+ fn ($ value ) => [$ value [0 ], self ::parseValue ($ value [1 ])],
92
+ $ parameters
93
+ ));
94
+ }
95
+
96
+ /**
97
+ * @param array{0:itemValue, 1:array<array{0:string, 1:itemValue}>}|itemValue $value
98
+ */
99
+ private static function parseItem (mixed $ value ): ?Item
100
+ {
101
+ return match (true ) {
102
+ !is_array ($ value ) => Item::new ($ value ),
103
+ [] === $ value => null ,
104
+ default => Item::new ([
105
+ self ::parseValue ($ value [0 ]),
106
+ self ::parseParameters ($ value [1 ]),
107
+ ]),
108
+ };
109
+ }
110
+
111
+ private static function parseInnerList (array $ innerListPair ): InnerList
112
+ {
113
+ return InnerList::fromPair ([
114
+ array_map (fn ($ value ) => self ::parseItem ($ value ), $ innerListPair [0 ]),
115
+ self ::parseParameters ($ innerListPair [1 ]),
116
+ ]);
117
+ }
118
+
119
+ private static function parseList (array $ listPairs ): OuterList
120
+ {
121
+ return OuterList::fromPairs (array_map (
122
+ fn ($ value ) => is_array ($ value [0 ]) && array_is_list ($ value [0 ]) ? self ::parseInnerList ($ value ) : self ::parseItem ($ value ),
123
+ $ listPairs
124
+ ));
125
+ }
126
+
127
+ private static function parseDictionary (array $ dictionaryPair ): Dictionary
128
+ {
129
+ return Dictionary::fromPairs (array_map (
130
+ fn ($ value ) => [
131
+ $ value [0 ],
132
+ is_array ($ value [1 ][0 ]) && array_is_list ($ value [1 ][0 ]) ? self ::parseInnerList ($ value [1 ]) : self ::parseItem ($ value [1 ]),
133
+ ],
134
+ $ dictionaryPair
135
+ ));
136
+ }
48
137
}
0 commit comments