From 56228ff141d61083071bd941db2918a4dc040143 Mon Sep 17 00:00:00 2001 From: Laszlo Teveli Date: Thu, 28 Jul 2022 12:15:49 +0200 Subject: [PATCH] Adopting primary associated types --- Sources/AsyncHTTP/Loader.swift | 10 ++++- Sources/AsyncHTTP/Loaders/Capture.swift | 20 +++++++-- Sources/AsyncHTTP/Loaders/Decode.swift | 24 +++++++++-- Sources/AsyncHTTP/Loaders/FlatMap.swift | 43 ++++++++++++++++--- Sources/AsyncHTTP/Loaders/HTTP.swift | 17 ++++++-- Sources/AsyncHTTP/Loaders/Intercept.swift | 11 ++++- Sources/AsyncHTTP/Loaders/Map.swift | 42 +++++++++++++++--- Sources/AsyncHTTP/Loaders/Pipe.swift | 11 ++++- Sources/AsyncHTTP/Loaders/Pullback.swift | 11 ++++- Sources/AsyncHTTP/Loaders/URLSession.swift | 12 +++++- Sources/AsyncHTTP/Plugins/Deduplication.swift | 9 +++- Sources/AsyncHTTP/Plugins/Delay.swift | 9 +++- .../AsyncHTTP/Plugins/IdentifyRequests.swift | 13 ++++-- Sources/AsyncHTTP/Plugins/Retry.swift | 25 +++++++---- .../AsyncHTTP/Plugins/ServerEnvironment.swift | 9 +++- Sources/AsyncHTTP/Plugins/Throttle.swift | 9 +++- Sources/AsyncHTTP/Plugins/Timeout.swift | 20 +++++++-- .../AsyncHTTP/Plugins/ValidateRequests.swift | 19 +++++--- Sources/AsyncHTTP/Utils/Converted.swift | 19 ++++++++ Tests/AsyncHTTPTests/ConvertedTests.swift | 38 ++++++++++++++-- .../HTTPNetworkExecutorTests.swift | 10 ++--- Tests/AsyncHTTPTests/StaticLoader.swift | 3 -- 22 files changed, 316 insertions(+), 68 deletions(-) diff --git a/Sources/AsyncHTTP/Loader.swift b/Sources/AsyncHTTP/Loader.swift index b7225f65..414b06b2 100644 --- a/Sources/AsyncHTTP/Loader.swift +++ b/Sources/AsyncHTTP/Loader.swift @@ -3,11 +3,19 @@ import Foundation import FoundationNetworking #endif -public protocol Loader { +#if compiler(>=5.7) +@rethrows public protocol Loader { associatedtype Input associatedtype Output func load(_ input: Input) async throws -> Output } +#else +@rethrows public protocol Loader { + associatedtype Input + associatedtype Output + func load(_ input: Input) async throws -> Output +} +#endif extension Loader { public func loadResult(_ input: Input) async -> Result { diff --git a/Sources/AsyncHTTP/Loaders/Capture.swift b/Sources/AsyncHTTP/Loaders/Capture.swift index 36ec2098..5def57ca 100644 --- a/Sources/AsyncHTTP/Loaders/Capture.swift +++ b/Sources/AsyncHTTP/Loaders/Capture.swift @@ -1,12 +1,26 @@ import Foundation extension Loader { +#if compiler(>=5.7) + @_disfavoredOverload + public func capture(input: @escaping (Input) -> Void) -> some Loader { + Loaders.Capture(self, captureInput: input) + } +#endif + public func capture(input: @escaping (Input) -> Void) -> Loaders.Capture { - Loaders.Capture(self, captureInput: input) + .init(self, captureInput: input) + } + +#if compiler(>=5.7) + @_disfavoredOverload + public func capture(output: @escaping (Input, Output) -> Void) -> some Loader { + Loaders.Capture(self, captureOutput: output) } +#endif public func capture(output: @escaping (Input, Output) -> Void) -> Loaders.Capture { - Loaders.Capture(self, captureOutput: output) + .init(self, captureOutput: output) } } @@ -27,7 +41,7 @@ extension Loaders { self.captureOutput = captureOutput } - public func load(_ input: Input) async throws -> Output { + public func load(_ input: Input) async rethrows -> Output { captureInput?(input) let output = try await original.load(input) captureOutput?(input, output) diff --git a/Sources/AsyncHTTP/Loaders/Decode.swift b/Sources/AsyncHTTP/Loaders/Decode.swift index a3293045..a614bf57 100644 --- a/Sources/AsyncHTTP/Loaders/Decode.swift +++ b/Sources/AsyncHTTP/Loaders/Decode.swift @@ -4,12 +4,28 @@ import Combine import Foundation extension Loader where Output: Decodable { - public func decode(using decoder: Decoder, to type: DecodedOutput.Type = DecodedOutput.self) -> Loaders.Decoded where Decoder.Input == Output { - Loaders.Decoded(self, decoder: decoder) +#if compiler(>=5.7) + @_disfavoredOverload + public func decode(using decoder: Decoder, to type: DecodedOutput.Type = DecodedOutput.self) -> some Loader where Decoder.Input == Output { + Loaders.Decoded(self, decoder: decoder) } +#endif + + public func decode(using decoder: Decoder, to type: DecodedOutput.Type = DecodedOutput.self) -> Loaders.Decoded where Decoder.Input == Output { + .init(self, decoder: decoder) + } +} + +extension Loader where Output == Data { +#if compiler(>=5.7) + @_disfavoredOverload + public func decode(using decoder: JSONDecoder = JSONDecoder(), to type: DecodedOutput.Type = DecodedOutput.self) -> some Loader { + Loaders.Decoded(self, decoder: decoder) + } +#endif - public func decode(using decoder: JSONDecoder = JSONDecoder(), to type: DecodedOutput.Type = DecodedOutput.self) -> Loaders.Decoded { - Loaders.Decoded(self, decoder: decoder) + public func decode(using decoder: JSONDecoder = JSONDecoder(), to type: DecodedOutput.Type = DecodedOutput.self) -> Loaders.Decoded { + .init(self, decoder: decoder) } } diff --git a/Sources/AsyncHTTP/Loaders/FlatMap.swift b/Sources/AsyncHTTP/Loaders/FlatMap.swift index 16cf44e8..bc52ea91 100644 --- a/Sources/AsyncHTTP/Loaders/FlatMap.swift +++ b/Sources/AsyncHTTP/Loaders/FlatMap.swift @@ -1,13 +1,31 @@ import Foundation extension Loader { - public func flatMap(_ transform: @escaping (Output) throws -> NewLoader) -> Loaders.FlatMap where Output == NewLoader.Input { - Loaders.FlatMap(self, transform) +#if compiler(>=5.7) + @_disfavoredOverload + public func flatMap(_ transform: @escaping (Output) throws -> NewLoader) -> some Loader where Output == NewLoader.Input { + Loaders.ThrowingFlatMap(self, transform) + } +#endif + + public func flatMap(_ transform: @escaping (Output) throws -> NewLoader) -> Loaders.ThrowingFlatMap where Output == NewLoader.Input { + .init(self, transform) + } + +#if compiler(>=5.7) + @_disfavoredOverload + public func flatMap(_ transform: @escaping (Output) -> NewLoader) -> some Loader where Output == NewLoader.Input { + Loaders.FlatMap(self, transform) + } +#endif + + public func flatMap(_ transform: @escaping (Output) -> NewLoader) -> Loaders.FlatMap where Output == NewLoader.Input { + .init(self, transform) } } extension Loaders { - public struct FlatMap: Loader where NewLoader.Input == Upstream.Output { + public struct ThrowingFlatMap: Loader where NewLoader.Input == Upstream.Output { public typealias Input = Upstream.Input private let upstream: Upstream @@ -23,6 +41,21 @@ extension Loaders { return try await transform(output).load(output) } } -} -extension Loaders.FlatMap: HTTPLoader where Input == HTTPRequest, Output == HTTPResponse {} + public struct FlatMap: Loader where NewLoader.Input == Upstream.Output { + public typealias Input = Upstream.Input + + private let upstream: Upstream + private let transform: (Upstream.Output) -> NewLoader + + init(_ upstream: Upstream, _ transform: @escaping (Upstream.Output) -> NewLoader) { + self.upstream = upstream + self.transform = transform + } + + public func load(_ input: Input) async rethrows -> NewLoader.Output { + let output = try await upstream.load(input) + return try await transform(output).load(output) + } + } +} diff --git a/Sources/AsyncHTTP/Loaders/HTTP.swift b/Sources/AsyncHTTP/Loaders/HTTP.swift index 0c8eba7b..584276b8 100644 --- a/Sources/AsyncHTTP/Loaders/HTTP.swift +++ b/Sources/AsyncHTTP/Loaders/HTTP.swift @@ -4,13 +4,24 @@ import FoundationNetworking #endif extension Loader where Input == URLRequest, Output == (Data, URLResponse) { - public func httpLoader(modify: ((HTTPRequest, inout URLRequest) -> Void)? = nil) -> Loaders.HTTP { .init(loader: self, modify: modify) } +#if compiler(>=5.7) + @_disfavoredOverload + public func httpLoader(modify: ((HTTPRequest, inout URLRequest) -> Void)? = nil) -> some HTTPLoader { + Loaders.HTTP(loader: self, modify: modify) + } +#endif + + public func httpLoader(modify: ((HTTPRequest, inout URLRequest) -> Void)? = nil) -> Loaders.HTTP { + Loaders.HTTP(loader: self, modify: modify) + } } -public protocol HTTPLoader: Loader where Input == HTTPRequest, Output == HTTPResponse {} +#if compiler(>=5.7) +public typealias HTTPLoader = Loader +#endif extension Loaders { - public struct HTTP: CompositeLoader, HTTPLoader where Wrapped.Input == URLRequest, Wrapped.Output == (Data, URLResponse) { + public struct HTTP: CompositeLoader where Wrapped.Input == URLRequest, Wrapped.Output == (Data, URLResponse) { private let loader: Wrapped private let modify: ((HTTPRequest, inout URLRequest) -> Void)? diff --git a/Sources/AsyncHTTP/Loaders/Intercept.swift b/Sources/AsyncHTTP/Loaders/Intercept.swift index 5f8fe9e3..43c9914b 100644 --- a/Sources/AsyncHTTP/Loaders/Intercept.swift +++ b/Sources/AsyncHTTP/Loaders/Intercept.swift @@ -1,8 +1,15 @@ import Foundation extension Loader { +#if compiler(>=5.7) + @_disfavoredOverload + public func intercept(transform: @escaping (inout Input) -> Void) -> some Loader { + Loaders.Intercept(self, transform: transform) + } +#endif + public func intercept(transform: @escaping (inout Input) -> Void) -> Loaders.Intercept { - Loaders.Intercept(self, transform: transform) + .init(self, transform: transform) } } @@ -20,7 +27,7 @@ extension Loaders { self.transform = transform } - public func load(_ input: Input) async throws -> Output { + public func load(_ input: Input) async rethrows -> Output { var modified = input transform?(&modified) return try await original.load(modified) diff --git a/Sources/AsyncHTTP/Loaders/Map.swift b/Sources/AsyncHTTP/Loaders/Map.swift index cc7c9d84..7c88a034 100644 --- a/Sources/AsyncHTTP/Loaders/Map.swift +++ b/Sources/AsyncHTTP/Loaders/Map.swift @@ -1,13 +1,31 @@ import Foundation extension Loader { - public func map(_ transform: @escaping (Output) throws -> NewOutput) -> Loaders.Map { - Loaders.Map(self, transform) +#if compiler(>=5.7) + @_disfavoredOverload + public func map(_ transform: @escaping (Output) throws -> NewOutput) -> some Loader { + Loaders.ThrowingMap(self, transform) + } +#endif + + public func map(_ transform: @escaping (Output) throws -> NewOutput) -> Loaders.ThrowingMap { + .init(self, transform) + } + +#if compiler(>=5.7) + @_disfavoredOverload + public func map(_ transform: @escaping (Output) -> NewOutput) -> some Loader { + Loaders.Map(self, transform) + } +#endif + + public func map(_ transform: @escaping (Output) -> NewOutput) -> Loaders.Map { + .init(self, transform) } } extension Loaders { - public struct Map: Loader { + public struct ThrowingMap: Loader { public typealias Input = Upstream.Input private let upstream: Upstream @@ -22,6 +40,20 @@ extension Loaders { try await transform(upstream.load(input)) } } -} -extension Loaders.Map: HTTPLoader where Input == HTTPRequest, Output == HTTPResponse {} + public struct Map: Loader { + public typealias Input = Upstream.Input + + private let upstream: Upstream + private let transform: (Upstream.Output) -> Output + + init(_ upstream: Upstream, _ transform: @escaping (Upstream.Output) -> Output) { + self.upstream = upstream + self.transform = transform + } + + public func load(_ input: Input) async rethrows -> Output { + try await transform(upstream.load(input)) + } + } +} diff --git a/Sources/AsyncHTTP/Loaders/Pipe.swift b/Sources/AsyncHTTP/Loaders/Pipe.swift index c819d092..5a05681f 100644 --- a/Sources/AsyncHTTP/Loaders/Pipe.swift +++ b/Sources/AsyncHTTP/Loaders/Pipe.swift @@ -1,9 +1,16 @@ import Foundation extension Loader { - public func pipe(_ other: Other) -> Loaders.Pipe { +#if compiler(>=5.7) + @_disfavoredOverload + public func pipe(_ other: Other) -> some Loader where Output == Other.Input { Loaders.Pipe(self, other) } +#endif + + public func pipe(_ other: Other) -> Loaders.Pipe where Output == Other.Input { + .init(self, other) + } } extension Loaders { @@ -19,7 +26,7 @@ extension Loaders { self.downstream = downstream } - public func load(_ input: Input) async throws -> Output { + public func load(_ input: Input) async rethrows -> Output { try await downstream.load(upstream.load(input)) } } diff --git a/Sources/AsyncHTTP/Loaders/Pullback.swift b/Sources/AsyncHTTP/Loaders/Pullback.swift index 99d006ab..40ba84df 100644 --- a/Sources/AsyncHTTP/Loaders/Pullback.swift +++ b/Sources/AsyncHTTP/Loaders/Pullback.swift @@ -1,8 +1,15 @@ import Foundation extension Loader { +#if compiler(>=5.7) + @_disfavoredOverload + public func pullback(transform: @escaping (NewInput) -> Input) -> some Loader { + Loaders.Pullback(self, transform) + } +#endif + public func pullback(transform: @escaping (NewInput) -> Input) -> Loaders.Pullback { - Loaders.Pullback(self, transform) + .init(self, transform) } } @@ -18,7 +25,7 @@ extension Loaders { self.transform = transform } - public func load(_ input: Input) async throws -> Output { + public func load(_ input: Input) async rethrows -> Output { try await downstream.load(transform(input)) } } diff --git a/Sources/AsyncHTTP/Loaders/URLSession.swift b/Sources/AsyncHTTP/Loaders/URLSession.swift index 6c044572..6a4c8b36 100644 --- a/Sources/AsyncHTTP/Loaders/URLSession.swift +++ b/Sources/AsyncHTTP/Loaders/URLSession.swift @@ -19,13 +19,23 @@ extension URLSession { #endif extension URLSession { +#if compiler(>=5.7) + public var dataLoader: some Loader { + Loaders.URLSessionData(urlSession: self) + } + + public var httpLoader: some HTTPLoader { + dataLoader.httpLoader() + } +#else public var dataLoader: Loaders.URLSessionData { - .init(urlSession: self) + Loaders.URLSessionData(urlSession: self) } public var httpLoader: Loaders.HTTP { dataLoader.httpLoader() } +#endif } extension Loaders { diff --git a/Sources/AsyncHTTP/Plugins/Deduplication.swift b/Sources/AsyncHTTP/Plugins/Deduplication.swift index 464f3713..2ce2f296 100644 --- a/Sources/AsyncHTTP/Plugins/Deduplication.swift +++ b/Sources/AsyncHTTP/Plugins/Deduplication.swift @@ -1,6 +1,13 @@ import Foundation extension Loader { +#if compiler(>=5.7) + @_disfavoredOverload + public func deduplicate() -> some Loader where Input: Hashable { + Loaders.Deduplicate(loader: self) + } +#endif + public func deduplicate() -> Loaders.Deduplicate where Input: Hashable { .init(loader: self) } @@ -30,5 +37,3 @@ extension Loaders { } } } - -extension Loaders.Deduplicate: HTTPLoader where Input == HTTPRequest, Output == HTTPResponse {} diff --git a/Sources/AsyncHTTP/Plugins/Delay.swift b/Sources/AsyncHTTP/Plugins/Delay.swift index 28cb7aab..499498ae 100644 --- a/Sources/AsyncHTTP/Plugins/Delay.swift +++ b/Sources/AsyncHTTP/Plugins/Delay.swift @@ -1,6 +1,13 @@ import Foundation extension Loader { +#if compiler(>=5.7) + @_disfavoredOverload + public func delay(seconds: TimeInterval) -> some Loader { + Loaders.Delay(loader: self, seconds: seconds) + } +#endif + public func delay(seconds: TimeInterval) -> Loaders.Delay { .init(loader: self, seconds: seconds) } @@ -26,5 +33,3 @@ extension Loaders { } } } - -extension Loaders.Delay: HTTPLoader where Input == HTTPRequest, Output == HTTPResponse {} diff --git a/Sources/AsyncHTTP/Plugins/IdentifyRequests.swift b/Sources/AsyncHTTP/Plugins/IdentifyRequests.swift index 5d4693e7..05d6f6f3 100644 --- a/Sources/AsyncHTTP/Plugins/IdentifyRequests.swift +++ b/Sources/AsyncHTTP/Plugins/IdentifyRequests.swift @@ -19,8 +19,15 @@ extension HTTPRequest: Identifiable { } } -extension Loader { - public func identifyRequests(generateIdentifier: @escaping (HTTPRequest) -> String = { _ in UUID().uuidString }) -> Loaders.ApplyRequestIdentity where Input == HTTPRequest { +extension Loader where Input == HTTPRequest { +#if compiler(>=5.7) + @_disfavoredOverload + public func identifyRequests(generateIdentifier: @escaping (HTTPRequest) -> String = { _ in UUID().uuidString }) -> some Loader { + Loaders.ApplyRequestIdentity(loader: self, generateIdentifier: generateIdentifier) + } +#endif + + public func identifyRequests(generateIdentifier: @escaping (HTTPRequest) -> String = { _ in UUID().uuidString }) -> Loaders.ApplyRequestIdentity { .init(loader: self, generateIdentifier: generateIdentifier) } } @@ -44,5 +51,3 @@ extension Loaders { } } } - -extension Loaders.ApplyRequestIdentity: HTTPLoader where Output == HTTPResponse {} diff --git a/Sources/AsyncHTTP/Plugins/Retry.swift b/Sources/AsyncHTTP/Plugins/Retry.swift index de4896d2..86cc8eb5 100644 --- a/Sources/AsyncHTTP/Plugins/Retry.swift +++ b/Sources/AsyncHTTP/Plugins/Retry.swift @@ -72,21 +72,33 @@ extension RetryStrategy where Self == Backoff { } } -@available(iOS 15.0, macOS 12.0, *) extension Loader { - @available(iOS 15.0, macOS 12.0, *) +#if compiler(>=5.7) + @_disfavoredOverload + public func applyRetryStrategy(default: RetryStrategy? = nil) -> some Loader { + Loaders.ApplyRetryStrategy(loader: self) { _ in `default` } + } +#endif + public func applyRetryStrategy(default: RetryStrategy? = nil) -> Loaders.ApplyRetryStrategy { .init(loader: self) { _ in `default` } } +} - @available(iOS 15.0, macOS 12.0, *) - public func applyRetryStrategy(default: RetryStrategy? = nil) -> Loaders.ApplyRetryStrategy where Input == HTTPRequest { +extension Loader where Input == HTTPRequest { +#if compiler(>=5.7) + @_disfavoredOverload + public func applyRetryStrategy(default: RetryStrategy? = nil) -> some Loader { + Loaders.ApplyRetryStrategy(loader: self) { $0.retryStrategy ?? `default` } + } +#endif + + public func applyRetryStrategy(default: RetryStrategy? = nil) -> Loaders.ApplyRetryStrategy { .init(loader: self) { $0.retryStrategy ?? `default` } } } extension Loaders { - @available(iOS 15.0, macOS 12.0, *) public struct ApplyRetryStrategy: CompositeLoader { private let loader: Wrapped private let retryStrategy: (Input) -> RetryStrategy? @@ -117,6 +129,3 @@ extension Loaders { } } } - -@available(iOS 15.0, macOS 12.0, *) -extension Loaders.ApplyRetryStrategy: HTTPLoader where Input == HTTPRequest, Output == HTTPResponse {} diff --git a/Sources/AsyncHTTP/Plugins/ServerEnvironment.swift b/Sources/AsyncHTTP/Plugins/ServerEnvironment.swift index ddebff00..c8c8ab53 100644 --- a/Sources/AsyncHTTP/Plugins/ServerEnvironment.swift +++ b/Sources/AsyncHTTP/Plugins/ServerEnvironment.swift @@ -41,6 +41,13 @@ extension HTTPRequest { } extension Loader where Input == HTTPRequest { +#if compiler(>=5.7) + @_disfavoredOverload + public func applyServerEnvironment(defaultEnvironment: ServerEnvironment? = nil) -> some Loader { + Loaders.ApplyServerEnvironment(loader: self) + } +#endif + public func applyServerEnvironment(defaultEnvironment: ServerEnvironment? = nil) -> Loaders.ApplyServerEnvironment { .init(loader: self) } @@ -65,8 +72,6 @@ extension Loaders { } } -extension Loaders.ApplyServerEnvironment: HTTPLoader where Output == HTTPResponse {} - private func +(lhs: [URLQueryItem]?, rhs: [URLQueryItem]?) -> [URLQueryItem]? { switch (lhs, rhs) { case let (.some(lhs), .some(rhs)): diff --git a/Sources/AsyncHTTP/Plugins/Throttle.swift b/Sources/AsyncHTTP/Plugins/Throttle.swift index 4ecb256e..fba53ded 100644 --- a/Sources/AsyncHTTP/Plugins/Throttle.swift +++ b/Sources/AsyncHTTP/Plugins/Throttle.swift @@ -1,6 +1,13 @@ import Foundation extension Loader { +#if compiler(>=5.7) + @_disfavoredOverload + public func throttle(maximumNumberOfRequests: UInt = UInt.max) -> some Loader where Input: Hashable { + Loaders.Throttle(loader: self, maximumNumberOfRequests: maximumNumberOfRequests) + } +#endif + public func throttle(maximumNumberOfRequests: UInt = UInt.max) -> Loaders.Throttle where Input: Hashable { .init(loader: self, maximumNumberOfRequests: maximumNumberOfRequests) } @@ -29,8 +36,6 @@ extension Loaders { } } -extension Loaders.Throttle: HTTPLoader where Input == HTTPRequest, Output == HTTPResponse {} - @propertyWrapper final class Published: AsyncSequence { typealias AsyncIterator = AsyncStream.Iterator diff --git a/Sources/AsyncHTTP/Plugins/Timeout.swift b/Sources/AsyncHTTP/Plugins/Timeout.swift index 0b9688b1..55b7f44f 100644 --- a/Sources/AsyncHTTP/Plugins/Timeout.swift +++ b/Sources/AsyncHTTP/Plugins/Timeout.swift @@ -39,11 +39,27 @@ extension HTTPRequest { } extension Loader { +#if compiler(>=5.7) + @_disfavoredOverload + public func applyTimeout(default: Timeout? = nil) -> some Loader { + Loaders.ApplyTimeout(loader: self) { _ in `default` } + } +#endif + public func applyTimeout(default: Timeout? = nil) -> Loaders.ApplyTimeout { .init(loader: self) { _ in `default` } } +} - public func applyTimeout(default: Timeout? = nil) -> Loaders.ApplyTimeout where Input == HTTPRequest { +extension Loader where Input == HTTPRequest { +#if compiler(>=5.7) + @_disfavoredOverload + public func applyTimeout(default: Timeout? = nil) -> some Loader { + Loaders.ApplyTimeout(loader: self) { $0.timeout ?? `default` } + } +#endif + + public func applyTimeout(default: Timeout? = nil) -> Loaders.ApplyTimeout { .init(loader: self) { $0.timeout ?? `default` } } } @@ -77,8 +93,6 @@ extension Loaders { } } -extension Loaders.ApplyTimeout: HTTPLoader where Input == HTTPRequest, Output == HTTPResponse {} - extension Task where Success == Never, Failure == Never { public static func sleep(seconds: TimeInterval) async throws { try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000)) diff --git a/Sources/AsyncHTTP/Plugins/ValidateRequests.swift b/Sources/AsyncHTTP/Plugins/ValidateRequests.swift index f47560d7..45e23ab3 100644 --- a/Sources/AsyncHTTP/Plugins/ValidateRequests.swift +++ b/Sources/AsyncHTTP/Plugins/ValidateRequests.swift @@ -1,21 +1,28 @@ import Foundation extension Loader where Input == HTTPRequest, Output == HTTPResponse { +#if compiler(>=5.7) + @_disfavoredOverload + public func validateRequests() -> some Loader { + Loaders.ValidateRequests(loader: self) + } +#endif + public func validateRequests() -> Loaders.ValidateRequests { - Loaders.ValidateRequests(self) + .init(loader: self) } } extension Loaders { - public struct ValidateRequests: HTTPLoader where Upstream.Input == HTTPRequest, Upstream.Output == HTTPResponse { - private let upstream: Upstream + public struct ValidateRequests: Loader where Upstream.Input == HTTPRequest, Upstream.Output == HTTPResponse { + private let loader: Upstream - init(_ upstream: Upstream) { - self.upstream = upstream + public init(loader: Upstream) { + self.loader = loader } public func load(_ request: HTTPRequest) async throws -> HTTPResponse { - let output = try await upstream.load(request) + let output = try await loader.load(request) let type = String(describing: type(of: self)) if request.timeout != nil, !type.contains("ApplyTimeout<") { throw LoaderValidationError.loaderShouldContainApplyTimeout diff --git a/Sources/AsyncHTTP/Utils/Converted.swift b/Sources/AsyncHTTP/Utils/Converted.swift index 203d3638..d68ab6b7 100644 --- a/Sources/AsyncHTTP/Utils/Converted.swift +++ b/Sources/AsyncHTTP/Utils/Converted.swift @@ -79,3 +79,22 @@ public extension KeyedEncodingContainer { } } } + +public struct Composed {} + +extension Composed: ConversionStrategy where A: ConversionStrategy, B: ConversionStrategy { + public typealias RawValue = A.RawValue + public typealias ConvertedValue = B.ConvertedValue +} + +extension Composed: EncoderStrategy where A: EncoderStrategy, B: EncoderStrategy, A.ConvertedValue == B.RawValue { + public static func encode(_ value: B.ConvertedValue) throws -> A.RawValue { + try A.encode(B.encode(value)) + } +} + +extension Composed: DecoderStrategy where A: DecoderStrategy, B: DecoderStrategy, A.ConvertedValue == B.RawValue { + public static func decode(_ value: A.RawValue) throws -> B.ConvertedValue { + try B.decode(A.decode(value)) + } +} diff --git a/Tests/AsyncHTTPTests/ConvertedTests.swift b/Tests/AsyncHTTPTests/ConvertedTests.swift index c896c529..b5be78ad 100644 --- a/Tests/AsyncHTTPTests/ConvertedTests.swift +++ b/Tests/AsyncHTTPTests/ConvertedTests.swift @@ -45,11 +45,11 @@ final class ConvertedTests: XCTestCase { let encoded = try encoder.encode(Request(date: Date(timeIntervalSince1970: 0))) // Then - XCTAssertEqual(encoded, #""" + XCTAssertEqual(encoded.string, #""" { "date" : "1970-01-01T00:00:00Z" } - """#.data) + """#) } func test_whenEncodingOptionalConvertedProperty_thenEncodesCorrectly() throws { @@ -62,7 +62,24 @@ final class ConvertedTests: XCTestCase { let encoded = try JSONEncoder().encode(Request()) // Then - XCTAssertEqual(encoded, "{}".data) + XCTAssertEqual(encoded.string, "{}") + } + + func test_whenDecodingConvertedPropertyWithComposition_thenDecodesCorrectly() throws { + // Given + struct Response: Decodable { + @Converted> var day: Int + } + + // When + let decoded = try JSONDecoder().decode(Response.self, from: #""" + { + "day": "1970-01-13T00:00:00Z" + } + """#.data) + + // Then + XCTAssertEqual(decoded.day, 13) } } @@ -72,6 +89,12 @@ extension String { } } +extension Data { + var string: String? { + String(data: self, encoding: .utf8) + } +} + private struct ISO8601: TwoWayConversionStrategy { typealias RawValue = String typealias ConvertedValue = Date @@ -91,3 +114,12 @@ private struct ISO8601: TwoWayConversionStrategy { struct ConversionError: Error {} } + +private struct Day: DecoderStrategy { + typealias RawValue = Date + typealias ConvertedValue = Int + + static func decode(_ value: Date) throws -> Int { + Calendar(identifier: .gregorian).dateComponents([.day], from: value).day! + } +} diff --git a/Tests/AsyncHTTPTests/HTTPNetworkExecutorTests.swift b/Tests/AsyncHTTPTests/HTTPNetworkExecutorTests.swift index 7b2d0b87..89a195a1 100644 --- a/Tests/AsyncHTTPTests/HTTPNetworkExecutorTests.swift +++ b/Tests/AsyncHTTPTests/HTTPNetworkExecutorTests.swift @@ -32,7 +32,7 @@ final class HTTPNetworkExecutorTests: XCTestCase { $0.retryStrategy = .immediately(maximumNumberOfAttempts: 5) } var loadedRequest: HTTPRequest? - let loader: some HTTPLoader = testLoader + let loader = testLoader .capture { loadedRequest = $0 } .applyServerEnvironment() .applyTimeout() @@ -66,18 +66,18 @@ final class HTTPNetworkExecutorTests: XCTestCase { XCTAssertEqual(loadedRequest?.id, response.id) } - func test_whenDecodingResponse_thenItSucceeds() async throws { + func test_whenDecodingResponse_thenItSucceeds() throws { // Given struct CustomResponse: Decodable, Equatable { let key: String } - let loader: some HTTPLoader = StaticLoader(Data(#"{"key": "value"}"#.utf8), .dummy(headers: [ + let loader = StaticLoader(Data(#"{"key": "value"}"#.utf8), .dummy(headers: [ "User-Agent": "X", "Set-Cookie": "a=b; Domain=google.com; Path=/; Secure; HttpOnly" ])) // When - let response = try await loader.load(HTTPRequest()) + let response = loader.load(HTTPRequest()) // Then XCTAssertEqual(try response.jsonBody(), CustomResponse(key: "value")) @@ -90,7 +90,7 @@ final class HTTPNetworkExecutorTests: XCTestCase { struct CustomResponse: Decodable, Equatable { let key: String } - let httpLoader: some HTTPLoader = StaticLoader(Data(#"{"key": "value"}"#.utf8), .dummy()) + let httpLoader = StaticLoader(Data(#"{"key": "value"}"#.utf8), .dummy()) let loader: AnyLoader = httpLoader .map(\.body) .decode() diff --git a/Tests/AsyncHTTPTests/StaticLoader.swift b/Tests/AsyncHTTPTests/StaticLoader.swift index 96c7c0d0..c09bd7b5 100644 --- a/Tests/AsyncHTTPTests/StaticLoader.swift +++ b/Tests/AsyncHTTPTests/StaticLoader.swift @@ -14,6 +14,3 @@ struct StaticLoader: Loader { HTTPResponse(request: request, response: response, body: data) } } - -extension StaticLoader: HTTPLoader {} -