Skip to content

Commit f07f692

Browse files
committed
Make tokens strings (it's easier)
1 parent cf08d29 commit f07f692

File tree

6 files changed

+52
-37
lines changed

6 files changed

+52
-37
lines changed

Sources/StructuredHeaders/ComponentTypes.swift

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public enum BareItem<BaseData: RandomAccessCollection> where BaseData.Element ==
3232
case decimal(PseudoDecimal)
3333
case string(String)
3434
case undecodedByteSequence(BaseData)
35-
case token(BaseData)
35+
case token(String)
3636
}
3737

3838
extension BareItem: ExpressibleByBooleanLiteral {
@@ -55,7 +55,11 @@ extension BareItem: ExpressibleByFloatLiteral {
5555

5656
extension BareItem: ExpressibleByStringLiteral {
5757
public init(stringLiteral value: StringLiteralType) {
58-
self = .string(value)
58+
if value.isValidToken {
59+
self = .token(value)
60+
} else {
61+
self = .string(value)
62+
}
5963
}
6064
}
6165

@@ -155,3 +159,42 @@ public struct InnerList<BaseData: RandomAccessCollection>: Hashable where BaseDa
155159
self.parameters = parameters
156160
}
157161
}
162+
163+
extension String {
164+
var isValidToken: Bool {
165+
let view = self.utf8
166+
167+
switch view.first {
168+
case .some(asciiCapitals), .some(asciiLowercases), .some(asciiAsterisk):
169+
// Good
170+
()
171+
default:
172+
return false
173+
}
174+
175+
for byte in view {
176+
switch byte {
177+
// Valid token characters are RFC 7230 tchar, colon, and slash.
178+
// tchar is:
179+
//
180+
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
181+
// / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
182+
// / DIGIT / ALPHA
183+
//
184+
// The following insane case statement covers this. Tokens suck.
185+
case asciiExclamationMark, asciiOctothorpe, asciiDollar, asciiPercent,
186+
asciiAmpersand, asciiSquote, asciiAsterisk, asciiPlus, asciiDash,
187+
asciiPeriod, asciiCaret, asciiUnderscore, asciiBacktick, asciiPipe,
188+
asciiTilde, asciiDigits, asciiCapitals, asciiLowercases,
189+
asciiColon, asciiSlash:
190+
// Good
191+
()
192+
default:
193+
// Bad token
194+
return false
195+
}
196+
}
197+
198+
return true
199+
}
200+
}

Sources/StructuredHeaders/Decoder/BareItemDecoder.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ extension BareItemDecoder: SingleValueDecodingContainer {
8181
case .string(let string):
8282
return string
8383
case .token(let token):
84-
return String(decoding: token, as: UTF8.self)
84+
return token
8585
default:
8686
throw StructuredHeaderError.invalidTypeForItem
8787
}

Sources/StructuredHeaders/FieldParser.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ extension StructuredFieldParser {
443443
// we get to the end of the string.
444444
let tokenSlice = self.underlyingData[..<index]
445445
self.underlyingData = self.underlyingData[index...]
446-
return .token(tokenSlice)
446+
return .token(String(decoding: tokenSlice, as: UTF8.self))
447447
}
448448

449449
private mutating func _parseParameters() throws -> OrderedMap<BaseData.SubSequence, BareItem> {

Sources/StructuredHeaders/FieldSerializer.swift

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -176,38 +176,10 @@ extension StructuredFieldSerializer {
176176
}
177177
self.data.append(asciiDquote)
178178
case .token(let token):
179-
switch token.first {
180-
case .some(asciiCapitals), .some(asciiLowercases), .some(asciiAsterisk):
181-
// Good
182-
()
183-
default:
179+
guard token.isValidToken else {
184180
throw StructuredHeaderError.invalidToken
185181
}
186-
187-
for byte in token {
188-
switch byte {
189-
// Valid token characters are RFC 7230 tchar, colon, and slash.
190-
// tchar is:
191-
//
192-
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
193-
// / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
194-
// / DIGIT / ALPHA
195-
//
196-
// The following insane case statement covers this. Tokens suck.
197-
case asciiExclamationMark, asciiOctothorpe, asciiDollar, asciiPercent,
198-
asciiAmpersand, asciiSquote, asciiAsterisk, asciiPlus, asciiDash,
199-
asciiPeriod, asciiCaret, asciiUnderscore, asciiBacktick, asciiPipe,
200-
asciiTilde, asciiDigits, asciiCapitals, asciiLowercases,
201-
asciiColon, asciiSlash:
202-
// Good
203-
()
204-
default:
205-
// Bad token
206-
throw StructuredHeaderError.invalidToken
207-
}
208-
}
209-
210-
self.data.append(contentsOf: token)
182+
self.data.append(contentsOf: token.utf8)
211183
case .undecodedByteSequence(let bytes):
212184
// We require the user to have gotten this right.
213185
self.data.append(asciiColon)

Tests/StructuredHeadersTests/StructuredFieldParserTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ final class StructuredFieldParserTests: XCTestCase {
3838
XCTAssertEqual(Double(baseDecimal), Double(jsonInteger), "\(fixtureName): Got \(bareItem), expected \(schema)")
3939
case (.string(let baseString), .string(let jsonString)):
4040
XCTAssertEqual(baseString, jsonString, "\(fixtureName): Got \(bareItem), expected \(schema)")
41-
case (.token(let baseToken), .dictionary(let typeDictionary)):
41+
case (.token(let token), .dictionary(let typeDictionary)):
4242
guard typeDictionary.count == 2, case .string(let typeName) = typeDictionary["__type"], case .string(let typeValue) = typeDictionary["value"] else {
4343
XCTFail("\(fixtureName): Unexpected type dict \(typeDictionary)")
4444
return
4545
}
4646

4747
XCTAssertEqual(typeName, "token", "\(fixtureName): Expected type token, got type \(typeName)")
48-
XCTAssertEqual(typeValue, String(decoding: baseToken, as: UTF8.self), "\(fixtureName): Got \(String(decoding: baseToken, as: UTF8.self)), expected \(typeValue)")
48+
XCTAssertEqual(typeValue, token, "\(fixtureName): Got \(token), expected \(typeValue)")
4949
case (.undecodedByteSequence(let binary), .dictionary(let typeDictionary)):
5050
guard typeDictionary.count == 2, case .string(let typeName) = typeDictionary["__type"], case .string(let typeValue) = typeDictionary["value"] else {
5151
XCTFail("\(fixtureName): Unexpected type dict \(typeDictionary)")

Tests/StructuredHeadersTests/StructuredFieldSerializerTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ extension BareItem where BaseData == ArraySlice<UInt8> {
193193
case .dictionary(let typeObject):
194194
switch (typeObject["__type"], typeObject["value"]) {
195195
case (.some(.string("token")), .some(.string(let value))):
196-
self = .token(ArraySlice(value.utf8))
196+
self = .token(value)
197197

198198
case (.some(.string("binary")), .some(.string(let value))):
199199
let expectedBase64Bytes = Data(base32Encoded: Data(value.utf8)).base64EncodedData()

0 commit comments

Comments
 (0)