From d84b8374d4e6556981f323bf4371515ac195da06 Mon Sep 17 00:00:00 2001 From: tom doron Date: Tue, 4 Aug 2020 21:48:36 -0700 Subject: [PATCH 1/3] remove requirment on macOS 10.13 motivation: simplify downstream libraries changes: use custom ISO8601 decoders on macOS < 10.13 --- Package.swift | 3 - .../AWSLambdaEvents/Utils/DateWrappers.swift | 85 +++++++++++++++++-- .../Utils/DateWrapperTests.swift | 35 +++++++- 3 files changed, 112 insertions(+), 11 deletions(-) diff --git a/Package.swift b/Package.swift index 1d56806e..0e5823f6 100644 --- a/Package.swift +++ b/Package.swift @@ -4,9 +4,6 @@ import PackageDescription let package = Package( name: "swift-aws-lambda-runtime", - platforms: [ - .macOS(.v10_13), - ], products: [ // this library exports `AWSLambdaRuntimeCore` and adds Foundation convenience methods .library(name: "AWSLambdaRuntime", targets: ["AWSLambdaRuntime"]), diff --git a/Sources/AWSLambdaEvents/Utils/DateWrappers.swift b/Sources/AWSLambdaEvents/Utils/DateWrappers.swift index e8432cf8..dd63c8b8 100644 --- a/Sources/AWSLambdaEvents/Utils/DateWrappers.swift +++ b/Sources/AWSLambdaEvents/Utils/DateWrappers.swift @@ -12,6 +12,12 @@ // //===----------------------------------------------------------------------===// +#if os(Linux) +import Glibc +#else +import Darwin.C +#endif + import struct Foundation.Date import class Foundation.DateFormatter import class Foundation.ISO8601DateFormatter @@ -28,14 +34,46 @@ public struct ISO8601Coding: Decodable { public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let dateString = try container.decode(String.self) - guard let date = Self.dateFormatter.date(from: dateString) else { + guard let date = Self.decodeDate(from: dateString) else { throw DecodingError.dataCorruptedError(in: container, debugDescription: - "Expected date to be in iso8601 date format, but `\(dateString)` does not forfill format") + "Expected date to be in ISO8601 date format, but `\(dateString)` is not in the correct format") } self.wrappedValue = date } + private static func decodeDate(from string: String) -> Date? { + #if os(Linux) + return Self.dateFormatter.date(from: string) + #elseif os(macOS) + if #available(macOS 10.12, *) { + return Self.dateFormatter.date(from: string) + } else { + return self.decodeISO8601Date(from: string).flatMap(Date.init(timeIntervalSince1970:)) + } + #endif + } + + @available(macOS 10.12, *) private static let dateFormatter = ISO8601DateFormatter() + + // strptime not avail on through Glibc + #if os(macOS) + // 1970-01-01T00:00:00Z + internal static func decodeISO8601Date(from string: String) -> Double? { + if string.last != "Z" { + return nil + } + var parsedTime = tm() + _ = string.withCString { cstr in + strptime(cstr, "%Y-%m-%dT%H:%M:%S", &parsedTime) + } + let time = timegm(&parsedTime) + if time == -1 { + return nil + } + return Double(time) + } + #endif } @propertyWrapper @@ -49,14 +87,29 @@ public struct ISO8601WithFractionalSecondsCoding: Decodable { public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let dateString = try container.decode(String.self) - guard let date = Self.dateFormatter.date(from: dateString) else { + guard let date = Self.decodeDate(from: dateString) else { throw DecodingError.dataCorruptedError(in: container, debugDescription: - "Expected date to be in iso8601 date format with fractional seconds, but `\(dateString)` does not forfill format") + "Expected date to be in ISO8601 date format with fractional seconds, but `\(dateString)` is not in the correct format") } self.wrappedValue = date } + private static func decodeDate(from string: String) -> Date? { + #if os(Linux) + return Self.dateFormatter.date(from: string) + #elseif os(macOS) + if #available(macOS 10.13, *) { + return self.dateFormatter.date(from: string) + } else { + return self.decodeISO8601Date(from: string).flatMap(Date.init(timeIntervalSince1970:)) + } + #endif + } + + @available(macOS 10.13, *) private static let dateFormatter: ISO8601DateFormatter = Self.createDateFormatter() + + @available(macOS 10.13, *) private static func createDateFormatter() -> ISO8601DateFormatter { let formatter = ISO8601DateFormatter() formatter.formatOptions = [ @@ -68,6 +121,28 @@ public struct ISO8601WithFractionalSecondsCoding: Decodable { ] return formatter } + + // strptime not avail on through Glibc + #if os(macOS) + // 1970-01-01T00:00:00.00Z + internal static func decodeISO8601Date(from string: String) -> Double? { + guard let msIndex = string.lastIndex(of: ".") else { + return nil + } + guard let endIndex = string.lastIndex(of: "Z") else { + return nil + } + if endIndex <= msIndex { + return nil + } + let msString = string[msIndex ..< endIndex] + guard let ms = Double(msString) else { + return nil + } + + return ISO8601Coding.decodeISO8601Date(from: string)?.advanced(by: ms) + } + #endif } @propertyWrapper @@ -88,7 +163,7 @@ public struct RFC5322DateTimeCoding: Decodable { } guard let date = Self.dateFormatter.date(from: string) else { throw DecodingError.dataCorruptedError(in: container, debugDescription: - "Expected date to be in RFC5322 date-time format with fractional seconds, but `\(string)` does not forfill format") + "Expected date to be in RFC5322 date-time format with fractional seconds, but `\(string)` is not in the correct format") } self.wrappedValue = date } diff --git a/Tests/AWSLambdaEventsTests/Utils/DateWrapperTests.swift b/Tests/AWSLambdaEventsTests/Utils/DateWrapperTests.swift index fef0b2ba..9d31c020 100644 --- a/Tests/AWSLambdaEventsTests/Utils/DateWrapperTests.swift +++ b/Tests/AWSLambdaEventsTests/Utils/DateWrapperTests.swift @@ -43,11 +43,22 @@ class DateWrapperTests: XCTestCase { } XCTAssertEqual(context.codingPath.compactMap { $0.stringValue }, ["date"]) - XCTAssertEqual(context.debugDescription, "Expected date to be in iso8601 date format, but `\(date)` does not forfill format") + XCTAssertEqual(context.debugDescription, "Expected date to be in ISO8601 date format, but `\(date)` is not in the correct format") XCTAssertNil(context.underlyingError) } } + #if os(macOS) + @available(macOS 10.12, *) + func testISO8601CustomDecoder() { + let formatter = ISO8601DateFormatter() + let string = formatter.string(from: Date()) + let date = formatter.date(from: string) + let date2 = ISO8601Coding.decodeISO8601Date(from: string) + XCTAssertEqual(date?.timeIntervalSince1970, date2) + } + #endif + func testISO8601WithFractionalSecondsCodingWrapperSuccess() { struct TestEvent: Decodable { @ISO8601WithFractionalSecondsCoding @@ -75,11 +86,29 @@ class DateWrapperTests: XCTestCase { } XCTAssertEqual(context.codingPath.compactMap { $0.stringValue }, ["date"]) - XCTAssertEqual(context.debugDescription, "Expected date to be in iso8601 date format with fractional seconds, but `\(date)` does not forfill format") + XCTAssertEqual(context.debugDescription, "Expected date to be in ISO8601 date format with fractional seconds, but `\(date)` is not in the correct format") XCTAssertNil(context.underlyingError) } } + #if os(macOS) + @available(macOS 10.13, *) + func testISO8601WithFractionalSecondsCustomDecoder() { + let formatter = ISO8601DateFormatter() + formatter.formatOptions = [ + .withInternetDateTime, + .withDashSeparatorInDate, + .withColonSeparatorInTime, + .withColonSeparatorInTimeZone, + .withFractionalSeconds, + ] + let string = formatter.string(from: Date()) + let date = formatter.date(from: string) + let date2 = ISO8601WithFractionalSecondsCoding.decodeISO8601Date(from: string) + XCTAssertEqual(date?.timeIntervalSince1970, date2) + } + #endif + func testRFC5322DateTimeCodingWrapperSuccess() { struct TestEvent: Decodable { @RFC5322DateTimeCoding @@ -133,7 +162,7 @@ class DateWrapperTests: XCTestCase { } XCTAssertEqual(context.codingPath.compactMap { $0.stringValue }, ["date"]) - XCTAssertEqual(context.debugDescription, "Expected date to be in RFC5322 date-time format with fractional seconds, but `\(date)` does not forfill format") + XCTAssertEqual(context.debugDescription, "Expected date to be in RFC5322 date-time format with fractional seconds, but `\(date)` is not in the correct format") XCTAssertNil(context.underlyingError) } } From e2951d6faf64321eb2d4572dd39d42db67e7cc34 Mon Sep 17 00:00:00 2001 From: tom doron Date: Mon, 10 Aug 2020 09:44:22 -0700 Subject: [PATCH 2/3] fixup --- .../AWSLambdaEvents/Utils/DateWrappers.swift | 47 ++----------------- .../Utils/DateWrapperTests.swift | 29 ------------ 2 files changed, 4 insertions(+), 72 deletions(-) diff --git a/Sources/AWSLambdaEvents/Utils/DateWrappers.swift b/Sources/AWSLambdaEvents/Utils/DateWrappers.swift index dd63c8b8..564cce19 100644 --- a/Sources/AWSLambdaEvents/Utils/DateWrappers.swift +++ b/Sources/AWSLambdaEvents/Utils/DateWrappers.swift @@ -48,32 +48,14 @@ public struct ISO8601Coding: Decodable { if #available(macOS 10.12, *) { return Self.dateFormatter.date(from: string) } else { - return self.decodeISO8601Date(from: string).flatMap(Date.init(timeIntervalSince1970:)) + // unlikely *debugging* use case of swift 5.2+ on older macOS + preconditionFailure("Unsporrted macOS version") } #endif } @available(macOS 10.12, *) private static let dateFormatter = ISO8601DateFormatter() - - // strptime not avail on through Glibc - #if os(macOS) - // 1970-01-01T00:00:00Z - internal static func decodeISO8601Date(from string: String) -> Double? { - if string.last != "Z" { - return nil - } - var parsedTime = tm() - _ = string.withCString { cstr in - strptime(cstr, "%Y-%m-%dT%H:%M:%S", &parsedTime) - } - let time = timegm(&parsedTime) - if time == -1 { - return nil - } - return Double(time) - } - #endif } @propertyWrapper @@ -101,7 +83,8 @@ public struct ISO8601WithFractionalSecondsCoding: Decodable { if #available(macOS 10.13, *) { return self.dateFormatter.date(from: string) } else { - return self.decodeISO8601Date(from: string).flatMap(Date.init(timeIntervalSince1970:)) + // unlikely *debugging* use case of swift 5.2+ on older macOS + preconditionFailure("Unsporrted macOS version") } #endif } @@ -121,28 +104,6 @@ public struct ISO8601WithFractionalSecondsCoding: Decodable { ] return formatter } - - // strptime not avail on through Glibc - #if os(macOS) - // 1970-01-01T00:00:00.00Z - internal static func decodeISO8601Date(from string: String) -> Double? { - guard let msIndex = string.lastIndex(of: ".") else { - return nil - } - guard let endIndex = string.lastIndex(of: "Z") else { - return nil - } - if endIndex <= msIndex { - return nil - } - let msString = string[msIndex ..< endIndex] - guard let ms = Double(msString) else { - return nil - } - - return ISO8601Coding.decodeISO8601Date(from: string)?.advanced(by: ms) - } - #endif } @propertyWrapper diff --git a/Tests/AWSLambdaEventsTests/Utils/DateWrapperTests.swift b/Tests/AWSLambdaEventsTests/Utils/DateWrapperTests.swift index 9d31c020..35b1a474 100644 --- a/Tests/AWSLambdaEventsTests/Utils/DateWrapperTests.swift +++ b/Tests/AWSLambdaEventsTests/Utils/DateWrapperTests.swift @@ -48,17 +48,6 @@ class DateWrapperTests: XCTestCase { } } - #if os(macOS) - @available(macOS 10.12, *) - func testISO8601CustomDecoder() { - let formatter = ISO8601DateFormatter() - let string = formatter.string(from: Date()) - let date = formatter.date(from: string) - let date2 = ISO8601Coding.decodeISO8601Date(from: string) - XCTAssertEqual(date?.timeIntervalSince1970, date2) - } - #endif - func testISO8601WithFractionalSecondsCodingWrapperSuccess() { struct TestEvent: Decodable { @ISO8601WithFractionalSecondsCoding @@ -91,24 +80,6 @@ class DateWrapperTests: XCTestCase { } } - #if os(macOS) - @available(macOS 10.13, *) - func testISO8601WithFractionalSecondsCustomDecoder() { - let formatter = ISO8601DateFormatter() - formatter.formatOptions = [ - .withInternetDateTime, - .withDashSeparatorInDate, - .withColonSeparatorInTime, - .withColonSeparatorInTimeZone, - .withFractionalSeconds, - ] - let string = formatter.string(from: Date()) - let date = formatter.date(from: string) - let date2 = ISO8601WithFractionalSecondsCoding.decodeISO8601Date(from: string) - XCTAssertEqual(date?.timeIntervalSince1970, date2) - } - #endif - func testRFC5322DateTimeCodingWrapperSuccess() { struct TestEvent: Decodable { @RFC5322DateTimeCoding From 649158b0a9d72637ce034563a415ba98370ef8b7 Mon Sep 17 00:00:00 2001 From: tom doron Date: Mon, 10 Aug 2020 09:52:37 -0700 Subject: [PATCH 3/3] fixup --- Sources/AWSLambdaEvents/Utils/DateWrappers.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Sources/AWSLambdaEvents/Utils/DateWrappers.swift b/Sources/AWSLambdaEvents/Utils/DateWrappers.swift index 564cce19..d5bc22f6 100644 --- a/Sources/AWSLambdaEvents/Utils/DateWrappers.swift +++ b/Sources/AWSLambdaEvents/Utils/DateWrappers.swift @@ -12,12 +12,6 @@ // //===----------------------------------------------------------------------===// -#if os(Linux) -import Glibc -#else -import Darwin.C -#endif - import struct Foundation.Date import class Foundation.DateFormatter import class Foundation.ISO8601DateFormatter