Skip to content

Commit f069b10

Browse files
authored
[RFC 9651] Implement adjustments for backward compatible update (#40)
Motivation: `swift-http-structured-headers` has to be updated to [RFC 9651](https://www.ietf.org/rfc/rfc9651.html). And it will be convenient for users to have this update as a SemVer minor. Modifications: - Deprecate the `BareItem` type and replace it with `RFC9651BareItem`, retaining existing behaviours. Updates for the new RFC will be based on `RFC9651BareItem`. Result: The library can be updated to the new RFC in a backward-compatible manner.
1 parent 8cc27e2 commit f069b10

File tree

11 files changed

+218
-53
lines changed

11 files changed

+218
-53
lines changed

Sources/RawStructuredFieldValues/ComponentTypes.swift

Lines changed: 160 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ extension ItemOrInnerList: Hashable {}
3232

3333
/// `BareItem` is a representation of the base data types at the bottom of a structured
3434
/// header field. These types are not parameterised: they are raw data.
35+
@available(*, deprecated, renamed: "RFC9651BareItem")
3536
public enum BareItem: Sendable {
3637
/// A boolean item.
3738
case bool(Bool)
@@ -53,24 +54,28 @@ public enum BareItem: Sendable {
5354
case token(String)
5455
}
5556

57+
@available(*, deprecated)
5658
extension BareItem: ExpressibleByBooleanLiteral {
5759
public init(booleanLiteral value: Bool) {
5860
self = .bool(value)
5961
}
6062
}
6163

64+
@available(*, deprecated)
6265
extension BareItem: ExpressibleByIntegerLiteral {
6366
public init(integerLiteral value: Int) {
6467
self = .integer(value)
6568
}
6669
}
6770

71+
@available(*, deprecated)
6872
extension BareItem: ExpressibleByFloatLiteral {
6973
public init(floatLiteral value: Float64) {
7074
self = .decimal(.init(floatLiteral: value))
7175
}
7276
}
7377

78+
@available(*, deprecated)
7479
extension BareItem: ExpressibleByStringLiteral {
7580
public init(stringLiteral value: StringLiteralType) {
7681
if value.structuredHeadersIsValidToken {
@@ -81,23 +86,157 @@ extension BareItem: ExpressibleByStringLiteral {
8186
}
8287
}
8388

89+
@available(*, deprecated)
90+
extension BareItem {
91+
init(transforming newItem: RFC9651BareItem) throws {
92+
switch newItem {
93+
case .bool(let b):
94+
self = .bool(b)
95+
96+
case .integer(let i):
97+
self = .integer(i)
98+
99+
case .decimal(let d):
100+
self = .decimal(d)
101+
102+
case .string(let s):
103+
self = .string(s)
104+
105+
case .undecodedByteSequence(let s):
106+
self = .undecodedByteSequence(s)
107+
108+
case .token(let t):
109+
self = .token(t)
110+
}
111+
}
112+
}
113+
114+
@available(*, deprecated)
84115
extension BareItem: Hashable {}
85116

117+
/// `RFC9651BareItem` is a representation of the base data types at the bottom of a structured
118+
/// header field. These types are not parameterised: they are raw data.
119+
public enum RFC9651BareItem: Sendable {
120+
/// A boolean item.
121+
case bool(Bool)
122+
123+
/// An integer item.
124+
case integer(Int)
125+
126+
/// A decimal item.
127+
case decimal(PseudoDecimal)
128+
129+
/// A string item.
130+
case string(String)
131+
132+
/// A byte sequence. This case must contain base64-encoded data, as
133+
/// `StructuredHeaders` does not do base64 encoding or decoding.
134+
case undecodedByteSequence(String)
135+
136+
/// A token item.
137+
case token(String)
138+
}
139+
140+
extension RFC9651BareItem: ExpressibleByBooleanLiteral {
141+
public init(booleanLiteral value: Bool) {
142+
self = .bool(value)
143+
}
144+
}
145+
146+
extension RFC9651BareItem: ExpressibleByIntegerLiteral {
147+
public init(integerLiteral value: Int) {
148+
self = .integer(value)
149+
}
150+
}
151+
152+
extension RFC9651BareItem: ExpressibleByFloatLiteral {
153+
public init(floatLiteral value: Float64) {
154+
self = .decimal(.init(floatLiteral: value))
155+
}
156+
}
157+
158+
extension RFC9651BareItem: ExpressibleByStringLiteral {
159+
public init(stringLiteral value: StringLiteralType) {
160+
if value.structuredHeadersIsValidToken {
161+
self = .token(value)
162+
} else {
163+
self = .string(value)
164+
}
165+
}
166+
}
167+
168+
extension RFC9651BareItem {
169+
@available(*, deprecated)
170+
init(transforming oldItem: BareItem) throws {
171+
switch oldItem {
172+
case .bool(let b):
173+
self = .bool(b)
174+
175+
case .integer(let i):
176+
self = .integer(i)
177+
178+
case .decimal(let d):
179+
self = .decimal(d)
180+
181+
case .string(let s):
182+
self = .string(s)
183+
184+
case .undecodedByteSequence(let s):
185+
self = .undecodedByteSequence(s)
186+
187+
case .token(let t):
188+
self = .token(t)
189+
}
190+
}
191+
}
192+
193+
extension RFC9651BareItem: Hashable {}
194+
86195
// MARK: - Item
87196

88197
/// `Item` represents a structured header field item: a combination of a `bareItem`
89198
/// and some parameters.
90199
public struct Item: Sendable {
91200
/// The `BareItem` that this `Item` contains.
92-
public var bareItem: BareItem
201+
@available(*, deprecated, renamed: "rfc9651BareItem")
202+
public var bareItem: BareItem {
203+
get {
204+
try! .init(transforming: self.rfc9651BareItem)
205+
}
206+
set {
207+
try! self.rfc9651BareItem = .init(transforming: newValue)
208+
}
209+
}
93210

94211
/// The parameters associated with `bareItem`
95-
public var parameters: OrderedMap<String, BareItem>
212+
@available(*, deprecated, renamed: "rfc9651Parameters")
213+
public var parameters: OrderedMap<String, BareItem> {
214+
get {
215+
try! self.rfc9651Parameters.mapValues { try .init(transforming: $0) }
216+
}
217+
set {
218+
try! self.rfc9651Parameters = newValue.mapValues { try .init(transforming: $0) }
219+
}
220+
}
221+
222+
/// The `BareItem` that this `Item` contains.
223+
public var rfc9651BareItem: RFC9651BareItem
96224

225+
/// The parameters associated with `rfc9651BareItem`
226+
public var rfc9651Parameters: OrderedMap<String, RFC9651BareItem>
227+
228+
@available(*, deprecated)
97229
public init(bareItem: BareItem, parameters: OrderedMap<String, BareItem>) {
230+
self.rfc9651BareItem = .integer(1)
231+
self.rfc9651Parameters = OrderedMap()
98232
self.bareItem = bareItem
99233
self.parameters = parameters
100234
}
235+
236+
public init(bareItem: RFC9651BareItem, parameters: OrderedMap<String, RFC9651BareItem>) {
237+
self.rfc9651BareItem = bareItem
238+
self.rfc9651Parameters = parameters
239+
}
101240
}
102241

103242
extension Item: Hashable {}
@@ -184,12 +323,30 @@ public struct InnerList: Hashable, Sendable {
184323
public var bareInnerList: BareInnerList
185324

186325
/// The parameters associated with the `bareInnerList`.
187-
public var parameters: OrderedMap<String, BareItem>
326+
@available(*, deprecated, renamed: "rfc9651Parameters")
327+
public var parameters: OrderedMap<String, BareItem> {
328+
get {
329+
try! self.rfc9651Parameters.mapValues { try .init(transforming: $0) }
330+
}
331+
set {
332+
try! self.rfc9651Parameters = newValue.mapValues { try .init(transforming: $0) }
333+
}
334+
}
188335

336+
/// The parameters associated with the `bareInnerList`.
337+
public var rfc9651Parameters: OrderedMap<String, RFC9651BareItem>
338+
339+
@available(*, deprecated)
189340
public init(bareInnerList: BareInnerList, parameters: OrderedMap<String, BareItem>) {
341+
self.rfc9651Parameters = OrderedMap()
190342
self.bareInnerList = bareInnerList
191343
self.parameters = parameters
192344
}
345+
346+
public init(bareInnerList: BareInnerList, parameters: OrderedMap<String, RFC9651BareItem>) {
347+
self.bareInnerList = bareInnerList
348+
self.rfc9651Parameters = parameters
349+
}
193350
}
194351

195352
extension String {

Sources/RawStructuredFieldValues/FieldParser.swift

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ extension StructuredFieldValueParser: Sendable where BaseData: Sendable, BaseDat
2929

3030
extension StructuredFieldValueParser {
3131
// Helper typealiases to avoid the explosion of generic parameters
32+
@available(*, deprecated, renamed: "RFC9651BareItem")
3233
public typealias BareItem = RawStructuredFieldValues.BareItem
34+
public typealias RFC9651BareItem = RawStructuredFieldValues.RFC9651BareItem
3335
public typealias Item = RawStructuredFieldValues.Item
3436
public typealias BareInnerList = RawStructuredFieldValues.BareInnerList
3537
public typealias InnerList = RawStructuredFieldValues.InnerList
@@ -204,7 +206,7 @@ extension StructuredFieldValueParser {
204206
return Item(bareItem: bareItem, parameters: parameters)
205207
}
206208

207-
private mutating func _parseABareItem() throws -> BareItem {
209+
private mutating func _parseABareItem() throws -> RFC9651BareItem {
208210
guard let first = self.underlyingData.first else {
209211
throw StructuredHeaderError.invalidItem
210212
}
@@ -225,7 +227,7 @@ extension StructuredFieldValueParser {
225227
}
226228
}
227229

228-
private mutating func _parseAnIntegerOrDecimal() throws -> BareItem {
230+
private mutating func _parseAnIntegerOrDecimal() throws -> RFC9651BareItem {
229231
var sign = 1
230232
var type = IntegerOrDecimal.integer
231233

@@ -301,7 +303,7 @@ extension StructuredFieldValueParser {
301303
}
302304
}
303305

304-
private mutating func _parseAString() throws -> BareItem {
306+
private mutating func _parseAString() throws -> RFC9651BareItem {
305307
assert(self.underlyingData.first == asciiDquote)
306308
self.underlyingData.consumeFirst()
307309

@@ -376,7 +378,7 @@ extension StructuredFieldValueParser {
376378
}
377379
}
378380

379-
private mutating func _parseAByteSequence() throws -> BareItem {
381+
private mutating func _parseAByteSequence() throws -> RFC9651BareItem {
380382
assert(self.underlyingData.first == asciiColon)
381383
self.underlyingData.consumeFirst()
382384

@@ -406,7 +408,7 @@ extension StructuredFieldValueParser {
406408
throw StructuredHeaderError.invalidByteSequence
407409
}
408410

409-
private mutating func _parseABoolean() throws -> BareItem {
411+
private mutating func _parseABoolean() throws -> RFC9651BareItem {
410412
assert(self.underlyingData.first == asciiQuestionMark)
411413
self.underlyingData.consumeFirst()
412414

@@ -423,7 +425,7 @@ extension StructuredFieldValueParser {
423425
}
424426
}
425427

426-
private mutating func _parseAToken() throws -> BareItem {
428+
private mutating func _parseAToken() throws -> RFC9651BareItem {
427429
assert(asciiCapitals.contains(self.underlyingData.first!) || asciiLowercases.contains(self.underlyingData.first!) || self.underlyingData.first! == asciiAsterisk)
428430

429431
var index = self.underlyingData.startIndex
@@ -457,8 +459,8 @@ extension StructuredFieldValueParser {
457459
return .token(String(decoding: tokenSlice, as: UTF8.self))
458460
}
459461

460-
private mutating func _parseParameters() throws -> OrderedMap<Key, BareItem> {
461-
var parameters = OrderedMap<Key, BareItem>()
462+
private mutating func _parseParameters() throws -> OrderedMap<Key, RFC9651BareItem> {
463+
var parameters = OrderedMap<Key, RFC9651BareItem>()
462464

463465
// We want to loop while we still have bytes _and_ while the first character is asciiSemicolon.
464466
// This covers both.
@@ -467,7 +469,7 @@ extension StructuredFieldValueParser {
467469
self.underlyingData.consumeFirst()
468470
self.underlyingData.stripLeadingSpaces()
469471
let paramName = try self._parseAKey()
470-
var paramValue: BareItem = true
472+
var paramValue: RFC9651BareItem = true
471473

472474
if self.underlyingData.first == asciiEqual {
473475
self.underlyingData.consumeFirst()

Sources/RawStructuredFieldValues/FieldSerializer.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ extension StructuredFieldValueSerializer {
8080
for (name, value) in dictionary {
8181
try self.serializeAKey(name)
8282

83-
if case .item(let item) = value, case .bool(true) = item.bareItem {
84-
try self.serializeParameters(item.parameters)
83+
if case .item(let item) = value, case .bool(true) = item.rfc9651BareItem {
84+
try self.serializeParameters(item.rfc9651Parameters)
8585
} else {
8686
self.data.append(asciiEqual)
8787

@@ -137,15 +137,15 @@ extension StructuredFieldValueSerializer {
137137

138138
self.data.append(asciiCloseParenthesis)
139139

140-
try self.serializeParameters(innerList.parameters)
140+
try self.serializeParameters(innerList.rfc9651Parameters)
141141
}
142142

143143
private mutating func serializeAnItem(_ item: Item) throws {
144-
try self.serializeABareItem(item.bareItem)
145-
try self.serializeParameters(item.parameters)
144+
try self.serializeABareItem(item.rfc9651BareItem)
145+
try self.serializeParameters(item.rfc9651Parameters)
146146
}
147147

148-
private mutating func serializeParameters(_ parameters: OrderedMap<String, BareItem>) throws {
148+
private mutating func serializeParameters(_ parameters: OrderedMap<String, RFC9651BareItem>) throws {
149149
for (key, value) in parameters {
150150
self.data.append(asciiSemicolon)
151151
try self.serializeAKey(key)
@@ -166,7 +166,7 @@ extension StructuredFieldValueSerializer {
166166
self.data.append(contentsOf: key.utf8)
167167
}
168168

169-
private mutating func serializeABareItem(_ item: BareItem) throws {
169+
private mutating func serializeABareItem(_ item: RFC9651BareItem) throws {
170170
switch item {
171171
case .integer(let int):
172172
guard let wideInt = Int64(exactly: int), validIntegerRange.contains(wideInt) else {

Sources/RawStructuredFieldValues/OrderedMap.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ public struct OrderedMap<Key, Value> where Key: Hashable {
4949
}
5050
}
5151
}
52+
53+
func mapValues<NewValue>(_ body: (Value) throws -> NewValue) rethrows -> OrderedMap<Key, NewValue> {
54+
var returnValue = OrderedMap<Key, NewValue>()
55+
returnValue.backing = try self.backing.map { try .init(key: $0.key, value: body($0.value)) }
56+
return returnValue
57+
}
5258
}
5359

5460
// MARK: - Helper struct for storing elements

Sources/StructuredFieldValues/Decoder/BareItemDecoder.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ import Foundation
1515
import RawStructuredFieldValues
1616

1717
struct BareItemDecoder {
18-
private var item: BareItem
18+
private var item: RFC9651BareItem
1919

2020
private var _codingPath: [_StructuredHeaderCodingKey]
2121

22-
init(_ item: BareItem, codingPath: [_StructuredHeaderCodingKey]) {
22+
init(_ item: RFC9651BareItem, codingPath: [_StructuredHeaderCodingKey]) {
2323
self.item = item
2424
self._codingPath = codingPath
2525
}

Sources/StructuredFieldValues/Decoder/ParametersDecoder.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ import Foundation
1515
import RawStructuredFieldValues
1616

1717
struct ParametersDecoder<Key: CodingKey, BaseData: RandomAccessCollection> where BaseData.Element == UInt8 {
18-
private var parameters: OrderedMap<String, BareItem>
18+
private var parameters: OrderedMap<String, RFC9651BareItem>
1919

2020
private var decoder: _StructuredFieldDecoder<BaseData>
2121

22-
init(_ parameters: OrderedMap<String, BareItem>, decoder: _StructuredFieldDecoder<BaseData>) {
22+
init(_ parameters: OrderedMap<String, RFC9651BareItem>, decoder: _StructuredFieldDecoder<BaseData>) {
2323
self.parameters = parameters
2424
self.decoder = decoder
2525
}

0 commit comments

Comments
 (0)