Skip to content

[RFC 9651] Implement adjustments for backward compatible update #40

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

Merged
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
163 changes: 160 additions & 3 deletions Sources/RawStructuredFieldValues/ComponentTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ extension ItemOrInnerList: Hashable {}

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

@available(*, deprecated)
extension BareItem: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = .bool(value)
}
}

@available(*, deprecated)
extension BareItem: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self = .integer(value)
}
}

@available(*, deprecated)
extension BareItem: ExpressibleByFloatLiteral {
public init(floatLiteral value: Float64) {
self = .decimal(.init(floatLiteral: value))
}
}

@available(*, deprecated)
extension BareItem: ExpressibleByStringLiteral {
public init(stringLiteral value: StringLiteralType) {
if value.structuredHeadersIsValidToken {
Expand All @@ -81,23 +86,157 @@ extension BareItem: ExpressibleByStringLiteral {
}
}

@available(*, deprecated)
extension BareItem {
init(transforming newItem: RFC9651BareItem) throws {
switch newItem {
case .bool(let b):
self = .bool(b)

case .integer(let i):
self = .integer(i)

case .decimal(let d):
self = .decimal(d)

case .string(let s):
self = .string(s)

case .undecodedByteSequence(let s):
self = .undecodedByteSequence(s)

case .token(let t):
self = .token(t)
}
}
}

@available(*, deprecated)
extension BareItem: Hashable {}

/// `RFC9651BareItem` is a representation of the base data types at the bottom of a structured
/// header field. These types are not parameterised: they are raw data.
public enum RFC9651BareItem: Sendable {
/// A boolean item.
case bool(Bool)

/// An integer item.
case integer(Int)

/// A decimal item.
case decimal(PseudoDecimal)

/// A string item.
case string(String)

/// A byte sequence. This case must contain base64-encoded data, as
/// `StructuredHeaders` does not do base64 encoding or decoding.
case undecodedByteSequence(String)

/// A token item.
case token(String)
}

extension RFC9651BareItem: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = .bool(value)
}
}

extension RFC9651BareItem: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self = .integer(value)
}
}

extension RFC9651BareItem: ExpressibleByFloatLiteral {
public init(floatLiteral value: Float64) {
self = .decimal(.init(floatLiteral: value))
}
}

extension RFC9651BareItem: ExpressibleByStringLiteral {
public init(stringLiteral value: StringLiteralType) {
if value.structuredHeadersIsValidToken {
self = .token(value)
} else {
self = .string(value)
}
}
}

extension RFC9651BareItem {
@available(*, deprecated)
init(transforming oldItem: BareItem) throws {
switch oldItem {
case .bool(let b):
self = .bool(b)

case .integer(let i):
self = .integer(i)

case .decimal(let d):
self = .decimal(d)

case .string(let s):
self = .string(s)

case .undecodedByteSequence(let s):
self = .undecodedByteSequence(s)

case .token(let t):
self = .token(t)
}
}
}

extension RFC9651BareItem: Hashable {}

// MARK: - Item

/// `Item` represents a structured header field item: a combination of a `bareItem`
/// and some parameters.
public struct Item: Sendable {
/// The `BareItem` that this `Item` contains.
public var bareItem: BareItem
@available(*, deprecated, renamed: "rfc9651BareItem")
public var bareItem: BareItem {
get {
try! .init(transforming: self.rfc9651BareItem)
}
set {
try! self.rfc9651BareItem = .init(transforming: newValue)
}
}

/// The parameters associated with `bareItem`
public var parameters: OrderedMap<String, BareItem>
@available(*, deprecated, renamed: "rfc9651Parameters")
public var parameters: OrderedMap<String, BareItem> {
get {
try! self.rfc9651Parameters.mapValues { try .init(transforming: $0) }
}
set {
try! self.rfc9651Parameters = newValue.mapValues { try .init(transforming: $0) }
}
}

/// The `BareItem` that this `Item` contains.
public var rfc9651BareItem: RFC9651BareItem

/// The parameters associated with `rfc9651BareItem`
public var rfc9651Parameters: OrderedMap<String, RFC9651BareItem>

@available(*, deprecated)
public init(bareItem: BareItem, parameters: OrderedMap<String, BareItem>) {
self.rfc9651BareItem = .integer(1)
self.rfc9651Parameters = OrderedMap()
self.bareItem = bareItem
self.parameters = parameters
}

public init(bareItem: RFC9651BareItem, parameters: OrderedMap<String, RFC9651BareItem>) {
self.rfc9651BareItem = bareItem
self.rfc9651Parameters = parameters
}
}

extension Item: Hashable {}
Expand Down Expand Up @@ -184,12 +323,30 @@ public struct InnerList: Hashable, Sendable {
public var bareInnerList: BareInnerList

/// The parameters associated with the `bareInnerList`.
public var parameters: OrderedMap<String, BareItem>
@available(*, deprecated, renamed: "rfc9651Parameters")
public var parameters: OrderedMap<String, BareItem> {
get {
try! self.rfc9651Parameters.mapValues { try .init(transforming: $0) }
}
set {
try! self.rfc9651Parameters = newValue.mapValues { try .init(transforming: $0) }
}
}

/// The parameters associated with the `bareInnerList`.
public var rfc9651Parameters: OrderedMap<String, RFC9651BareItem>

@available(*, deprecated)
public init(bareInnerList: BareInnerList, parameters: OrderedMap<String, BareItem>) {
self.rfc9651Parameters = OrderedMap()
self.bareInnerList = bareInnerList
self.parameters = parameters
}

public init(bareInnerList: BareInnerList, parameters: OrderedMap<String, RFC9651BareItem>) {
self.bareInnerList = bareInnerList
self.rfc9651Parameters = parameters
}
}

extension String {
Expand Down
20 changes: 11 additions & 9 deletions Sources/RawStructuredFieldValues/FieldParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ extension StructuredFieldValueParser: Sendable where BaseData: Sendable, BaseDat

extension StructuredFieldValueParser {
// Helper typealiases to avoid the explosion of generic parameters
@available(*, deprecated, renamed: "RFC9651BareItem")
public typealias BareItem = RawStructuredFieldValues.BareItem
public typealias RFC9651BareItem = RawStructuredFieldValues.RFC9651BareItem
public typealias Item = RawStructuredFieldValues.Item
public typealias BareInnerList = RawStructuredFieldValues.BareInnerList
public typealias InnerList = RawStructuredFieldValues.InnerList
Expand Down Expand Up @@ -204,7 +206,7 @@ extension StructuredFieldValueParser {
return Item(bareItem: bareItem, parameters: parameters)
}

private mutating func _parseABareItem() throws -> BareItem {
private mutating func _parseABareItem() throws -> RFC9651BareItem {
guard let first = self.underlyingData.first else {
throw StructuredHeaderError.invalidItem
}
Expand All @@ -225,7 +227,7 @@ extension StructuredFieldValueParser {
}
}

private mutating func _parseAnIntegerOrDecimal() throws -> BareItem {
private mutating func _parseAnIntegerOrDecimal() throws -> RFC9651BareItem {
var sign = 1
var type = IntegerOrDecimal.integer

Expand Down Expand Up @@ -301,7 +303,7 @@ extension StructuredFieldValueParser {
}
}

private mutating func _parseAString() throws -> BareItem {
private mutating func _parseAString() throws -> RFC9651BareItem {
assert(self.underlyingData.first == asciiDquote)
self.underlyingData.consumeFirst()

Expand Down Expand Up @@ -376,7 +378,7 @@ extension StructuredFieldValueParser {
}
}

private mutating func _parseAByteSequence() throws -> BareItem {
private mutating func _parseAByteSequence() throws -> RFC9651BareItem {
assert(self.underlyingData.first == asciiColon)
self.underlyingData.consumeFirst()

Expand Down Expand Up @@ -406,7 +408,7 @@ extension StructuredFieldValueParser {
throw StructuredHeaderError.invalidByteSequence
}

private mutating func _parseABoolean() throws -> BareItem {
private mutating func _parseABoolean() throws -> RFC9651BareItem {
assert(self.underlyingData.first == asciiQuestionMark)
self.underlyingData.consumeFirst()

Expand All @@ -423,7 +425,7 @@ extension StructuredFieldValueParser {
}
}

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

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

private mutating func _parseParameters() throws -> OrderedMap<Key, BareItem> {
var parameters = OrderedMap<Key, BareItem>()
private mutating func _parseParameters() throws -> OrderedMap<Key, RFC9651BareItem> {
var parameters = OrderedMap<Key, RFC9651BareItem>()

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

if self.underlyingData.first == asciiEqual {
self.underlyingData.consumeFirst()
Expand Down
14 changes: 7 additions & 7 deletions Sources/RawStructuredFieldValues/FieldSerializer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ extension StructuredFieldValueSerializer {
for (name, value) in dictionary {
try self.serializeAKey(name)

if case .item(let item) = value, case .bool(true) = item.bareItem {
try self.serializeParameters(item.parameters)
if case .item(let item) = value, case .bool(true) = item.rfc9651BareItem {
try self.serializeParameters(item.rfc9651Parameters)
} else {
self.data.append(asciiEqual)

Expand Down Expand Up @@ -137,15 +137,15 @@ extension StructuredFieldValueSerializer {

self.data.append(asciiCloseParenthesis)

try self.serializeParameters(innerList.parameters)
try self.serializeParameters(innerList.rfc9651Parameters)
}

private mutating func serializeAnItem(_ item: Item) throws {
try self.serializeABareItem(item.bareItem)
try self.serializeParameters(item.parameters)
try self.serializeABareItem(item.rfc9651BareItem)
try self.serializeParameters(item.rfc9651Parameters)
}

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

private mutating func serializeABareItem(_ item: BareItem) throws {
private mutating func serializeABareItem(_ item: RFC9651BareItem) throws {
switch item {
case .integer(let int):
guard let wideInt = Int64(exactly: int), validIntegerRange.contains(wideInt) else {
Expand Down
6 changes: 6 additions & 0 deletions Sources/RawStructuredFieldValues/OrderedMap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ public struct OrderedMap<Key, Value> where Key: Hashable {
}
}
}

func mapValues<NewValue>(_ body: (Value) throws -> NewValue) rethrows -> OrderedMap<Key, NewValue> {
var returnValue = OrderedMap<Key, NewValue>()
returnValue.backing = try self.backing.map { try .init(key: $0.key, value: body($0.value)) }
return returnValue
}
}

// MARK: - Helper struct for storing elements
Expand Down
4 changes: 2 additions & 2 deletions Sources/StructuredFieldValues/Decoder/BareItemDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import Foundation
import RawStructuredFieldValues

struct BareItemDecoder {
private var item: BareItem
private var item: RFC9651BareItem

private var _codingPath: [_StructuredHeaderCodingKey]

init(_ item: BareItem, codingPath: [_StructuredHeaderCodingKey]) {
init(_ item: RFC9651BareItem, codingPath: [_StructuredHeaderCodingKey]) {
self.item = item
self._codingPath = codingPath
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/StructuredFieldValues/Decoder/ParametersDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import Foundation
import RawStructuredFieldValues

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

private var decoder: _StructuredFieldDecoder<BaseData>

init(_ parameters: OrderedMap<String, BareItem>, decoder: _StructuredFieldDecoder<BaseData>) {
init(_ parameters: OrderedMap<String, RFC9651BareItem>, decoder: _StructuredFieldDecoder<BaseData>) {
self.parameters = parameters
self.decoder = decoder
}
Expand Down
Loading