Skip to content

Add support for Date type to StructuredFieldValues #49

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
merged 3 commits into from
Nov 26, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ extension BareInnerListDecoder: UnkeyedDecodingContainer {
} else if type is Decimal.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Decimal.self) as! T
} else if type is Date.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Date.self) as! T
} else {
return try type.init(from: self.decoder)
}
Expand Down
10 changes: 10 additions & 0 deletions Sources/StructuredFieldValues/Decoder/BareItemDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ extension BareItemDecoder: SingleValueDecodingContainer {
)
}

func decode(_: Date.Type) throws -> Date {
guard case .date(let date) = self.item else {
throw StructuredHeaderError.invalidTypeForItem
}

return Date(timeIntervalSince1970: Double(date))
}

func decodeNil() -> Bool {
// Items are never nil.
false
Expand Down Expand Up @@ -172,6 +180,8 @@ extension BareItemDecoder: SingleValueDecodingContainer {
return try self.decode(Data.self) as! T
case is Decimal.Type:
return try self.decode(Decimal.self) as! T
case is Date.Type:
return try self.decode(Date.self) as! T
default:
throw StructuredHeaderError.invalidTypeForItem
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ extension DictionaryKeyedContainer: KeyedDecodingContainerProtocol {
} else if type is Decimal.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Decimal.self) as! T
} else if type is Date.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Date.self) as! T
} else {
return try type.init(from: self.decoder)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ extension KeyedInnerListDecoder: KeyedDecodingContainerProtocol {
} else if type is Decimal.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Decimal.self) as! T
} else if type is Date.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Date.self) as! T
} else {
return try type.init(from: self.decoder)
}
Expand Down
3 changes: 3 additions & 0 deletions Sources/StructuredFieldValues/Decoder/KeyedItemDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ extension KeyedItemDecoder: KeyedDecodingContainerProtocol {
} else if type is Decimal.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Decimal.self) as! T
} else if type is Date.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Date.self) as! T
} else {
return try type.init(from: self.decoder)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ extension KeyedTopLevelListDecoder: KeyedDecodingContainerProtocol {
} else if type is Decimal.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Decimal.self) as! T
} else if type is Date.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Date.self) as! T
} else {
return try type.init(from: self.decoder)
}
Expand Down
3 changes: 3 additions & 0 deletions Sources/StructuredFieldValues/Decoder/ParametersDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ extension ParametersDecoder: KeyedDecodingContainerProtocol {
} else if type is Decimal.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Decimal.self) as! T
} else if type is Date.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Date.self) as! T
} else {
return try type.init(from: self.decoder)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ extension StructuredFieldValueDecoder {
} else if type is Decimal.Type {
let container = try decoder.singleValueContainer()
return try container.decode(Decimal.self) as! StructuredField
} else if type is Date.Type {
let container = try decoder.singleValueContainer()
return try container.decode(Date.self) as! StructuredField
} else {
return try type.init(from: decoder)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ extension TopLevelListDecoder: UnkeyedDecodingContainer {
} else if type is Decimal.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Decimal.self) as! T
} else if type is Date.Type {
let container = try self.decoder.singleValueContainer()
return try container.decode(Date.self) as! T
} else {
return try type.init(from: self.decoder)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ class _StructuredFieldEncoder {
try self.encode(value)
} else if let value = data as? Decimal {
try self.encode(value)
} else if let value = data as? Date {
try self.encode(value)
} else {
try data.encode(to: self)
}
Expand Down Expand Up @@ -309,6 +311,11 @@ extension _StructuredFieldEncoder: SingleValueEncodingContainer {
try self.currentStackEntry.storage.insertBareItem(.decimal(pd))
}

func encode(_ data: Date) throws {
let date = Int(data.timeIntervalSince1970)
try self.currentStackEntry.storage.insertBareItem(.date(date))
}

func encode<T>(_ value: T) throws where T: Encodable {
switch value {
case let value as UInt8:
Expand Down Expand Up @@ -343,6 +350,8 @@ extension _StructuredFieldEncoder: SingleValueEncodingContainer {
try self.encode(value)
case let value as Decimal:
try self.encode(value)
case let value as Date:
try self.encode(value)
default:
throw StructuredHeaderError.invalidTypeForItem
}
Expand Down Expand Up @@ -466,6 +475,11 @@ extension _StructuredFieldEncoder {
try self.currentStackEntry.storage.appendBareItem(.decimal(pd))
}

func append(_ value: Date) throws {
let date = Int(value.timeIntervalSince1970)
try self.currentStackEntry.storage.appendBareItem(.date(date))
}

func append<T>(_ value: T) throws where T: Encodable {
switch value {
case let value as UInt8:
Expand Down Expand Up @@ -500,6 +514,8 @@ extension _StructuredFieldEncoder {
try self.append(value)
case let value as Decimal:
try self.append(value)
case let value as Date:
try self.append(value)
default:
// Some other codable type.
switch self.currentStackEntry.storage {
Expand Down Expand Up @@ -636,6 +652,12 @@ extension _StructuredFieldEncoder {
try self.currentStackEntry.storage.insertBareItem(.decimal(pd), atKey: key)
}

func encode(_ value: Date, forKey key: String) throws {
let key = self.sanitizeKey(key)
let date = Int(value.timeIntervalSince1970)
try self.currentStackEntry.storage.insertBareItem(.date(date), atKey: key)
}

func encode<T>(_ value: T, forKey key: String) throws where T: Encodable {
let key = self.sanitizeKey(key)

Expand Down Expand Up @@ -672,6 +694,8 @@ extension _StructuredFieldEncoder {
try self.encode(value, forKey: key)
case let value as Decimal:
try self.encode(value, forKey: key)
case let value as Date:
try self.encode(value, forKey: key)
default:
// Ok, we don't know what this is. This can only happen for a dictionary, or
// for anything with parameters, or for lists, or for inner lists.
Expand Down
121 changes: 121 additions & 0 deletions Tests/StructuredFieldValuesTests/StructuredFieldDecoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -490,4 +490,125 @@ final class StructuredFieldDecoderTests: XCTestCase {
try StructuredFieldValueDecoder().decode(DictionaryField.self, from: Array(headerField.utf8))
)
}

func testDecodingDateAsTopLevelData() throws {
let headerField = "@4294967296"
XCTAssertEqual(
ItemField(Date(timeIntervalSince1970: 4_294_967_296)),
try StructuredFieldValueDecoder().decode(from: Array(headerField.utf8))
)
}

func testDecodingDateAsParameterisedData() throws {
struct Item: StructuredFieldValue, Equatable {
static let structuredFieldType: StructuredFieldType = .item
var item: Date
var parameters: [String: Float]
}

let headerFieldNoParameters = "@4294967296"
let headerFieldParameters = "@4294967296;q=0.8"

XCTAssertEqual(
Item(
item: Date(timeIntervalSince1970: 4_294_967_296),
parameters: [:]
),
try StructuredFieldValueDecoder().decode(
Item.self,
from: Array(headerFieldNoParameters.utf8)
)
)

XCTAssertEqual(
Item(item: Date(timeIntervalSince1970: 4_294_967_296), parameters: ["q": 0.8]),
try StructuredFieldValueDecoder().decode(
Item.self,
from: Array(headerFieldParameters.utf8)
)
)
}

func testDecodingDateInParameterField() throws {
struct Item: StructuredFieldValue, Equatable {
static let structuredFieldType: StructuredFieldType = .item
var item: Int
var parameters: [String: Date]
}

let headerField = "1;q=@4294967296"
XCTAssertEqual(
Item(item: 1, parameters: ["q": Date(timeIntervalSince1970: 4_294_967_296)]),
try StructuredFieldValueDecoder().decode(Item.self, from: Array(headerField.utf8))
)
}

func testDecodingDateInOuterListRaw() throws {
let headerField = "@4294967296, @-1659578233"
XCTAssertEqual(
List(
[
Date(timeIntervalSince1970: 4_294_967_296),
Date(timeIntervalSince1970: -1_659_578_233),
]
),
try StructuredFieldValueDecoder().decode(from: Array(headerField.utf8))
)
}

func testDecodingDateInInnerListRaw() throws {
let headerField = "(@4294967296 @-1659578233), (@4294967296 @-1659578233)"
XCTAssertEqual(
List(
Array(
repeating: [
Date(timeIntervalSince1970: 4_294_967_296),
Date(timeIntervalSince1970: -1_659_578_233),
],
count: 2
)
),
try StructuredFieldValueDecoder().decode(from: Array(headerField.utf8))
)
}

func testDecodingDateInInnerListKeyed() throws {
struct ListField: Codable, Equatable {
var items: [Date]
var parameters: [String: Bool]
}
let headerField = "(@4294967296 @-1659578233);foo, (@4294967296 @-1659578233);foo"
XCTAssertEqual(
List(
Array(
repeating: ListField(
items: [
Date(timeIntervalSince1970: 4_294_967_296),
Date(timeIntervalSince1970: -1_659_578_233),
],
parameters: ["foo": true]
),
count: 2
)
),
try StructuredFieldValueDecoder().decode(from: Array(headerField.utf8))
)
}

func testDecodingDateInDictionaries() throws {
struct DictionaryField: StructuredFieldValue, Equatable {
static let structuredFieldType: StructuredFieldType = .dictionary
var bin: Date
var box: Date
}

let headerField = "bin=@4294967296, box=@-1659578233"
XCTAssertEqual(
DictionaryField(
bin: Date(timeIntervalSince1970: 4_294_967_296),
box: Date(timeIntervalSince1970: -1_659_578_233)
),
try StructuredFieldValueDecoder().decode(from: Array(headerField.utf8))
)
}
}
43 changes: 43 additions & 0 deletions Tests/StructuredFieldValuesTests/StructuredFieldEncoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ final class StructuredFieldEncoderTests: XCTestCase {

// Binary Data
XCTAssertEqual(Array(":AQIDBA==:".utf8), try encoder.encode(ItemField(Data([1, 2, 3, 4]))))

// Date
XCTAssertEqual(
Array("@4294967296".utf8),
try encoder.encode(ItemField(Date(timeIntervalSince1970: 4_294_967_296)))
)
XCTAssertEqual(
Array("@-1659578233".utf8),
try encoder.encode(ItemField(Date(timeIntervalSince1970: -1_659_578_233)))
)
}

func testEncodeKeyedItemHeader() throws {
Expand Down Expand Up @@ -96,6 +106,26 @@ final class StructuredFieldEncoderTests: XCTestCase {
Array(":AQIDBA==:".utf8),
try encoder.encode(KeyedItem(item: Data([1, 2, 3, 4]), parameters: [:]))
)

// Date
XCTAssertEqual(
Array("@4294967296;x".utf8),
try encoder.encode(
KeyedItem(
item: Date(timeIntervalSince1970: 4_294_967_296),
parameters: ["x": true]
)
)
)
XCTAssertEqual(
Array("@-1659578233".utf8),
try encoder.encode(
KeyedItem(
item: Date(timeIntervalSince1970: -1_659_578_233),
parameters: [:]
)
)
)
}

func testEncodeKeyedItemHeaderWithParamsAsStruct() throws {
Expand Down Expand Up @@ -189,6 +219,19 @@ final class StructuredFieldEncoderTests: XCTestCase {
Array(":AQIDBA==:, :BQYHCA==:".utf8),
try encoder.encode(List([Data([1, 2, 3, 4]), Data([5, 6, 7, 8])]))
)

// Date
XCTAssertEqual(
Array("@4294967296, @-1659578233".utf8),
try encoder.encode(
List(
[
Date(timeIntervalSince1970: 4_294_967_296),
Date(timeIntervalSince1970: -1_659_578_233),
]
)
)
)
}

func testListFieldInnerItemsWithDict() throws {
Expand Down
Loading