Skip to content

Commit 141f102

Browse files
committed
Support Decimal as well
1 parent e12def5 commit 141f102

11 files changed

+161
-4
lines changed

Sources/CodableStructuredHeaders/Decoder/BareInnerListDecoder.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ extension BareInnerListDecoder: UnkeyedDecodingContainer {
6969
if type is Data.Type {
7070
let container = try self.decoder.singleValueContainer()
7171
return try container.decode(Data.self) as! T
72+
} else if type is Decimal.Type {
73+
let container = try self.decoder.singleValueContainer()
74+
return try container.decode(Decimal.self) as! T
7275
} else {
7376
return try type.init(from: self.decoder)
7477
}

Sources/CodableStructuredHeaders/Decoder/BareItemDecoder.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,16 @@ extension BareItemDecoder: SingleValueDecodingContainer {
109109
return decoded
110110
}
111111

112+
func decode(_ type: Decimal.Type) throws -> Decimal {
113+
guard case .decimal(let pseudoDecimal) = self.item else {
114+
throw StructuredHeaderError.invalidTypeForItem
115+
}
116+
117+
return Decimal(sign: pseudoDecimal.mantissa > 0 ? .plus : .minus,
118+
exponent: Int(pseudoDecimal.exponent),
119+
significand: Decimal(pseudoDecimal.mantissa))
120+
}
121+
112122
func decodeNil() -> Bool {
113123
// Items are never nil.
114124
return false
@@ -146,9 +156,9 @@ extension BareItemDecoder: SingleValueDecodingContainer {
146156
return try self.decode(Bool.self) as! T
147157
case is Data.Type:
148158
return try self.decode(Data.self) as! T
159+
case is Decimal.Type:
160+
return try self.decode(Decimal.self) as! T
149161
default:
150-
// Some other codable type. Not sure what to do here yet.
151-
// TODO: What about binary data here? For now we'll ignore it.
152162
throw StructuredHeaderError.invalidTypeForItem
153163
}
154164
}

Sources/CodableStructuredHeaders/Decoder/DictionaryKeyedContainer.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ extension DictionaryKeyedContainer: KeyedDecodingContainerProtocol {
5252
if type is Data.Type {
5353
let container = try self.decoder.singleValueContainer()
5454
return try container.decode(Data.self) as! T
55+
} else if type is Decimal.Type {
56+
let container = try self.decoder.singleValueContainer()
57+
return try container.decode(Decimal.self) as! T
5558
} else {
5659
return try type.init(from: self.decoder)
5760
}

Sources/CodableStructuredHeaders/Decoder/KeyedInnerListDecoder.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ extension KeyedInnerListDecoder: KeyedDecodingContainerProtocol {
5757
if type is Data.Type {
5858
let container = try self.decoder.singleValueContainer()
5959
return try container.decode(Data.self) as! T
60+
} else if type is Decimal.Type {
61+
let container = try self.decoder.singleValueContainer()
62+
return try container.decode(Decimal.self) as! T
6063
} else {
6164
return try type.init(from: self.decoder)
6265
}

Sources/CodableStructuredHeaders/Decoder/KeyedItemDecoder.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ extension KeyedItemDecoder: KeyedDecodingContainerProtocol {
5757
if type is Data.Type {
5858
let container = try self.decoder.singleValueContainer()
5959
return try container.decode(Data.self) as! T
60+
} else if type is Decimal.Type {
61+
let container = try self.decoder.singleValueContainer()
62+
return try container.decode(Decimal.self) as! T
6063
} else {
6164
return try type.init(from: self.decoder)
6265
}

Sources/CodableStructuredHeaders/Decoder/ParametersDecoder.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ extension ParametersDecoder: KeyedDecodingContainerProtocol {
5252
if type is Data.Type {
5353
let container = try self.decoder.singleValueContainer()
5454
return try container.decode(Data.self) as! T
55+
} else if type is Decimal.Type {
56+
let container = try self.decoder.singleValueContainer()
57+
return try container.decode(Decimal.self) as! T
5558
} else {
5659
return try type.init(from: self.decoder)
5760
}

Sources/CodableStructuredHeaders/Decoder/StructuredFieldDecoder.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ extension StructuredFieldDecoder {
102102
if type is Data.Type {
103103
let container = try decoder.singleValueContainer()
104104
return try container.decode(Data.self) as! StructuredField
105+
} else if type is Decimal.Type {
106+
let container = try decoder.singleValueContainer()
107+
return try container.decode(Decimal.self) as! StructuredField
105108
} else {
106109
return try type.init(from: decoder)
107110
}

Sources/CodableStructuredHeaders/Decoder/TopLevelListDecoder.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ extension TopLevelListDecoder: UnkeyedDecodingContainer {
6969
if type is Data.Type {
7070
let container = try self.decoder.singleValueContainer()
7171
return try container.decode(Data.self) as! T
72+
} else if type is Decimal.Type {
73+
let container = try self.decoder.singleValueContainer()
74+
return try container.decode(Decimal.self) as! T
7275
} else {
7376
return try type.init(from: self.decoder)
7477
}

Sources/CodableStructuredHeaders/Encoder/StructuredFieldEncoder.swift

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,16 @@ class _StructuredFieldEncoder {
128128
fileprivate func encodeItemField<StructuredField: Encodable>(_ data: StructuredField) throws -> [UInt8] {
129129
self.push(key: .init(stringValue: ""), newStorage: .itemHeader)
130130

131-
// There's an awkward special hook here: if the outer type is `Data`,
131+
// There's an awkward special hook here: if the outer type is `Data` or `Decimal`,
132132
// we skip the regular encoding path. This is because otherwise `Data` will
133-
// ask for an unkeyed container and it all falls apart.
133+
// ask for an unkeyed container and `Decimal` for a keyed one,
134+
// and it all falls apart.
134135
//
135136
// Everything else goes through the normal flow.
136137
if let value = data as? Data {
137138
try self.encode(value)
139+
} else if let value = data as? Decimal {
140+
try self.encode(value)
138141
} else {
139142
try data.encode(to: self)
140143
}
@@ -260,6 +263,16 @@ extension _StructuredFieldEncoder: SingleValueEncodingContainer {
260263
try self.currentStackEntry.storage.insertBareItem(.undecodedByteSequence(encoded))
261264
}
262265

266+
func encode(_ data: Decimal) throws {
267+
let significand = (data.significand as NSNumber).intValue // Yes, really.
268+
guard let exponent = Int8(exactly: data.exponent) else {
269+
throw StructuredHeaderError.invalidIntegerOrDecimal
270+
}
271+
272+
let pd = PseudoDecimal(mantissa: significand, exponent: Int(exponent))
273+
try self.currentStackEntry.storage.insertBareItem(.decimal(pd))
274+
}
275+
263276
func encode<T>(_ value: T) throws where T : Encodable {
264277
switch value {
265278
case let value as UInt8:
@@ -292,6 +305,8 @@ extension _StructuredFieldEncoder: SingleValueEncodingContainer {
292305
try self.encode(value)
293306
case let value as Data:
294307
try self.encode(value)
308+
case let value as Decimal:
309+
try self.encode(value)
295310
default:
296311
throw StructuredHeaderError.invalidTypeForItem
297312
}
@@ -405,6 +420,16 @@ extension _StructuredFieldEncoder {
405420
try self.currentStackEntry.storage.appendBareItem(.undecodedByteSequence(value.base64EncodedData()))
406421
}
407422

423+
func append(_ value: Decimal) throws {
424+
let significand = (value.significand as NSNumber).intValue // Yes, really.
425+
guard let exponent = Int8(exactly: value.exponent) else {
426+
throw StructuredHeaderError.invalidIntegerOrDecimal
427+
}
428+
429+
let pd = PseudoDecimal(mantissa: significand, exponent: Int(exponent))
430+
try self.currentStackEntry.storage.appendBareItem(.decimal(pd))
431+
}
432+
408433
func append<T>(_ value: T) throws where T : Encodable {
409434
switch value {
410435
case let value as UInt8:
@@ -437,6 +462,8 @@ extension _StructuredFieldEncoder {
437462
try self.append(value)
438463
case let value as Data:
439464
try self.append(value)
465+
case let value as Decimal:
466+
try self.append(value)
440467
default:
441468
// Some other codable type.
442469
switch self.currentStackEntry.storage {
@@ -560,6 +587,16 @@ extension _StructuredFieldEncoder {
560587
try self.currentStackEntry.storage.insertBareItem(.undecodedByteSequence(value.base64EncodedData()), atKey: key)
561588
}
562589

590+
func encode(_ value: Decimal, forKey key: String) throws {
591+
let significand = (value.significand as NSNumber).intValue // Yes, really.
592+
guard let exponent = Int8(exactly: value.exponent) else {
593+
throw StructuredHeaderError.invalidIntegerOrDecimal
594+
}
595+
596+
let pd = PseudoDecimal(mantissa: significand, exponent: Int(exponent))
597+
try self.currentStackEntry.storage.insertBareItem(.decimal(pd), atKey: key)
598+
}
599+
563600
func encode<T>(_ value: T, forKey key: String) throws where T: Encodable {
564601
let key = self.sanitizeKey(key)
565602

@@ -594,6 +631,8 @@ extension _StructuredFieldEncoder {
594631
try self.encode(value, forKey: key)
595632
case let value as Data:
596633
try self.encode(value, forKey: key)
634+
case let value as Decimal:
635+
try self.encode(value, forKey: key)
597636
default:
598637
// Ok, we don't know what this is. This can only happen for a dictionary, or
599638
// for anything with parameters, or for inner lists.

Tests/StructuredHeadersTests/StructuredFieldDecoderTests.swift

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,4 +282,86 @@ final class StructuredFieldDecoderTests: XCTestCase {
282282
try StructuredFieldDecoder().decodeDictionaryField(DictionaryField.self, from: Array(headerField.utf8))
283283
)
284284
}
285+
286+
func testDecodingDecimalAsTopLevelData() throws {
287+
let headerField = "987654321.123"
288+
XCTAssertEqual(
289+
Decimal(string: "987654321.123")!,
290+
try StructuredFieldDecoder().decodeItemField(Decimal.self, from: Array(headerField.utf8))
291+
)
292+
}
293+
294+
func testDecodingDecimalAsParameterisedData() throws {
295+
struct Item: Codable, Equatable {
296+
var item: Decimal
297+
var parameters: [String: Float]
298+
}
299+
300+
let headerFieldNoParameters = "987654321.123"
301+
let headerFieldParameters = "987654321.123;q=0.8"
302+
303+
XCTAssertEqual(
304+
Item(item: Decimal(string: "987654321.123")!, parameters: [:]),
305+
try StructuredFieldDecoder().decodeItemField(Item.self, from: Array(headerFieldNoParameters.utf8))
306+
)
307+
308+
XCTAssertEqual(
309+
Item(item: Decimal(string: "987654321.123")!, parameters: ["q": 0.8]),
310+
try StructuredFieldDecoder().decodeItemField(Item.self, from: Array(headerFieldParameters.utf8))
311+
)
312+
}
313+
314+
func testDecodingDecimalInParameterField() throws {
315+
struct Item: Codable, Equatable {
316+
var item: Int
317+
var parameters: [String: Decimal]
318+
}
319+
320+
let headerField = "1;q=987654321.123"
321+
XCTAssertEqual(
322+
Item(item: 1, parameters: ["q": Decimal(string: "987654321.123")!]),
323+
try StructuredFieldDecoder().decodeItemField(Item.self, from: Array(headerField.utf8))
324+
)
325+
}
326+
327+
func testDecodingDecimalInOuterListRaw() throws {
328+
let headerField = "987654321.123, 123456789.321"
329+
XCTAssertEqual(
330+
[Decimal(string: "987654321.123")!, Decimal(string: "123456789.321")!],
331+
try StructuredFieldDecoder().decodeListField([Decimal].self, from: Array(headerField.utf8))
332+
)
333+
}
334+
335+
func testDecodingDecimalInInnerListRaw() throws {
336+
let headerField = "(987654321.123 123456789.321), (987654321.123 123456789.321)"
337+
XCTAssertEqual(
338+
Array(repeating: [Decimal(string: "987654321.123")!, Decimal(string: "123456789.321")!], count: 2),
339+
try StructuredFieldDecoder().decodeListField([[Decimal]].self, from: Array(headerField.utf8))
340+
)
341+
}
342+
343+
func testDecodingDecimalInInnerListKeyed() throws {
344+
struct ListField: Codable, Equatable {
345+
var items: [Decimal]
346+
var parameters: [String: Bool]
347+
}
348+
let headerField = "(987654321.123 123456789.321);foo, (987654321.123 123456789.321);foo"
349+
XCTAssertEqual(
350+
Array(repeating: ListField(items: [Decimal(string: "987654321.123")!, Decimal(string: "123456789.321")!], parameters: ["foo": true]), count: 2),
351+
try StructuredFieldDecoder().decodeListField([ListField].self, from: Array(headerField.utf8))
352+
)
353+
}
354+
355+
func testDecodingDecimalInDictionaries() throws {
356+
struct DictionaryField: Codable, Equatable {
357+
var bin: Decimal
358+
var box: Decimal
359+
}
360+
361+
let headerField = "bin=987654321.123, box=123456789.321"
362+
XCTAssertEqual(
363+
DictionaryField(bin: Decimal(string: "987654321.123")!, box: Decimal(string: "123456789.321")!),
364+
try StructuredFieldDecoder().decodeDictionaryField(DictionaryField.self, from: Array(headerField.utf8))
365+
)
366+
}
285367
}

0 commit comments

Comments
 (0)