From 598ca122f0efe18f7caf60926e16b07b96aa129a Mon Sep 17 00:00:00 2001 From: Clive Date: Mon, 29 Jul 2024 22:35:52 -0400 Subject: [PATCH 1/5] Implement AsyncMapErrorSequence --- Evolution/NNNN-map-error.md | 85 ++++++++++++++ .../AsyncMapErrorSequence.swift | 105 ++++++++++++++++++ Tests/AsyncAlgorithmsTests/TestMapError.swift | 105 ++++++++++++++++++ 3 files changed, 295 insertions(+) create mode 100644 Evolution/NNNN-map-error.md create mode 100644 Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift create mode 100644 Tests/AsyncAlgorithmsTests/TestMapError.swift diff --git a/Evolution/NNNN-map-error.md b/Evolution/NNNN-map-error.md new file mode 100644 index 00000000..2d4c344a --- /dev/null +++ b/Evolution/NNNN-map-error.md @@ -0,0 +1,85 @@ +# Map Error + +* Proposal: [SAA-NNNN](NNNN-map-error.md) +* Authors: [Clive Liu](https://github.com/clive819) +* Review Manager: TBD +* Status: **Awaiting review** + +*During the review process, add the following fields as needed:* + +* Implementation: [apple/swift-async-algorithms#324](https://github.com/apple/swift-async-algorithms/pull/324) +* Decision Notes: +* Bugs: + +## Introduction + +The `mapError` function empowers developers to elegantly transform errors within asynchronous sequences, enhancing code readability and maintainability. + +```swift +extension AsyncSequence { + + public func mapError(_ transform: @Sendable @escaping (Self.Failure) -> MappedFailure) -> some AsyncSequence { + AsyncMapErrorSequence(base: self, transform: transform) + } +} +``` + +## Detailed design + +The function iterates through the elements of an `AsyncSequence` within a do-catch block. If an error is caught, it calls the `transform` closure to convert the error into a new type and then throws it. + +```swift +struct AsyncMapErrorSequence: AsyncSequence { + + ... + + func makeAsyncIterator() -> Iterator { + Iterator( + base: base.makeAsyncIterator(), + transform: transform + ) + } +} + +extension AsyncMapErrorSequence { + + struct Iterator: AsyncIteratorProtocol { + + typealias Element = Base.Element + + private var base: Base.AsyncIterator + + private let transform: @Sendable (Failure) -> MappedFailure + + init( + base: Base.AsyncIterator, + transform: @Sendable @escaping (Failure) -> MappedFailure + ) { + self.base = base + self.transform = transform + } + + mutating func next() async throws(MappedFailure) -> Element? { + do { + return try await base.next(isolation: nil) + } catch { + throw transform(error) + } + } + + mutating func next(isolation actor: isolated (any Actor)?) async throws(MappedFailure) -> Element? { + do { + return try await base.next(isolation: actor) + } catch { + throw transform(error) + } + } + } +} + +extension AsyncMapErrorSequence: Sendable where Base: Sendable, Base.Element: Sendable {} +``` + +## Naming + +The naming follows to current method naming of the Combine [mapError](https://developer.apple.com/documentation/combine/publisher/maperror(_:)) method. diff --git a/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift b/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift new file mode 100644 index 00000000..75008f41 --- /dev/null +++ b/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift @@ -0,0 +1,105 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if compiler(>=6.0) +extension AsyncSequence { + + /// Converts any failure into a new error. + /// + /// - Parameter transform: A closure that takes the failure as a parameter and returns a new error. + /// - Returns: An asynchronous sequence that maps the error thrown into the one produced by the transform closure. + /// + /// Use the ``mapError(_:)`` operator when you need to replace one error type with another. + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) + public func mapError(_ transform: @Sendable @escaping (Self.Failure) -> MappedError) -> some AsyncSequence { + AsyncMapErrorSequence(base: self, transform: transform) + } + + /// Converts any failure into a new error. + /// + /// - Parameter transform: A closure that takes the failure as a parameter and returns a new error. + /// - Returns: An asynchronous sequence that maps the error thrown into the one produced by the transform closure. + /// + /// Use the ``mapError(_:)`` operator when you need to replace one error type with another. + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) + public func mapError(_ transform: @Sendable @escaping (Self.Failure) -> MappedError) -> (some AsyncSequence & Sendable) where Self: Sendable, Self.Element: Sendable { + AsyncMapErrorSequence(base: self, transform: transform) + } +} + +/// An asynchronous sequence that converts any failure into a new error. +@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +fileprivate struct AsyncMapErrorSequence: AsyncSequence { + + typealias AsyncIterator = Iterator + typealias Element = Base.Element + typealias Failure = Base.Failure + + private let base: Base + private let transform: @Sendable (Failure) -> MappedError + + init( + base: Base, + transform: @Sendable @escaping (Failure) -> MappedError + ) { + self.base = base + self.transform = transform + } + + func makeAsyncIterator() -> Iterator { + Iterator( + base: base.makeAsyncIterator(), + transform: transform + ) + } +} + +@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +extension AsyncMapErrorSequence { + + /// The iterator that produces elements of the map sequence. + fileprivate struct Iterator: AsyncIteratorProtocol { + + typealias Element = Base.Element + + private var base: Base.AsyncIterator + + private let transform: @Sendable (Failure) -> MappedError + + init( + base: Base.AsyncIterator, + transform: @Sendable @escaping (Failure) -> MappedError + ) { + self.base = base + self.transform = transform + } + + mutating func next() async throws(MappedError) -> Element? { + try await self.next(isolation: nil) + } + + mutating func next(isolation actor: isolated (any Actor)?) async throws(MappedError) -> Element? { + do { + return try await base.next(isolation: actor) + } catch { + throw transform(error) + } + } + } +} + +@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +extension AsyncMapErrorSequence: Sendable where Base: Sendable, Base.Element: Sendable {} + +@available(*, unavailable) +extension AsyncMapErrorSequence.Iterator: Sendable {} +#endif diff --git a/Tests/AsyncAlgorithmsTests/TestMapError.swift b/Tests/AsyncAlgorithmsTests/TestMapError.swift new file mode 100644 index 00000000..afe90e13 --- /dev/null +++ b/Tests/AsyncAlgorithmsTests/TestMapError.swift @@ -0,0 +1,105 @@ +import AsyncAlgorithms +import XCTest + +#if compiler(>=6.0) +@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +final class TestMapError: XCTestCase { + + func test_mapError() async throws { + let array: [Any] = [1, 2, 3, MyAwesomeError.originalError, 4, 5, 6] + let sequence = array.async + .map { + if let error = $0 as? Error { + throw error + } else { + $0 as! Int + } + } + .mapError { _ in + MyAwesomeError.mappedError + } + + var results: [Int] = [] + + do { + for try await number in sequence { + results.append(number) + } + XCTFail("sequence should throw") + } catch { + XCTAssertEqual(error, .mappedError) + } + + XCTAssertEqual(results, [1, 2, 3]) + } + + func test_mapError_cancellation() async throws { + let value = "test" + let source = Indefinite(value: value).async + let sequence = source + .map { + if $0 == "just to trick compiler that this may throw" { + throw MyAwesomeError.originalError + } else { + $0 + } + } + .mapError { _ in + MyAwesomeError.mappedError + } + + let finished = expectation(description: "finished") + let iterated = expectation(description: "iterated") + + let task = Task { + var firstIteration = false + for try await el in sequence { + XCTAssertEqual(el, value) + + if !firstIteration { + firstIteration = true + iterated.fulfill() + } + } + finished.fulfill() + } + + // ensure the other task actually starts + await fulfillment(of: [iterated], timeout: 1.0) + // cancellation should ensure the loop finishes + // without regards to the remaining underlying sequence + task.cancel() + await fulfillment(of: [finished], timeout: 1.0) + } + + func test_mapError_empty() async throws { + let array: [String] = [] + let sequence = array.async + .map { + if $0 == "just to trick compiler that this may throw" { + throw MyAwesomeError.originalError + } else { + $0 + } + } + .mapError { _ in + MyAwesomeError.mappedError + } + + var results: [String] = [] + for try await value in sequence { + results.append(value) + } + XCTAssert(results.isEmpty) + } +} + +@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +private extension TestMapError { + + enum MyAwesomeError: Error { + case originalError + case mappedError + } +} +#endif From 6a958fd09e402a00906c8218a19b70bdb3949e37 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Thu, 11 Sep 2025 13:22:43 -0700 Subject: [PATCH 2/5] Update the proposal, implementation and tests with formatting, additional details and modifications for being able to push it to a review --- Evolution/NNNN-map-error.md | 80 +++------ .../AsyncMapErrorSequence.swift | 131 +++++++-------- Tests/AsyncAlgorithmsTests/TestMapError.swift | 156 +++++++++--------- 3 files changed, 159 insertions(+), 208 deletions(-) diff --git a/Evolution/NNNN-map-error.md b/Evolution/NNNN-map-error.md index 2d4c344a..04d59163 100644 --- a/Evolution/NNNN-map-error.md +++ b/Evolution/NNNN-map-error.md @@ -1,85 +1,49 @@ # Map Error * Proposal: [SAA-NNNN](NNNN-map-error.md) -* Authors: [Clive Liu](https://github.com/clive819) +* Authors: [Clive Liu](https://github.com/clive819) [Philippe Hausler](https://github.com/phausler) * Review Manager: TBD * Status: **Awaiting review** -*During the review process, add the following fields as needed:* +## Introduction -* Implementation: [apple/swift-async-algorithms#324](https://github.com/apple/swift-async-algorithms/pull/324) -* Decision Notes: -* Bugs: +Asynchronous sequences often particpate in carefully crafted systems of algorithms spliced together. Some of those algorithms require that both the element type and the failure type are the same as each-other. In order to line those types up for the element we have the map algorithm today, however there are other times at which when using more specific APIs that transforming the Failure type is needed. -## Introduction +## Motivation The `mapError` function empowers developers to elegantly transform errors within asynchronous sequences, enhancing code readability and maintainability. -```swift -extension AsyncSequence { +Building failure-type-safe versions of zip or other algorithms will need to require that the associated Failure types are the same. Having an effecient and easy to use transformation routine to adjust the failure-types is then key to delivering or interfacing with those failure-type-safe algorithms. - public func mapError(_ transform: @Sendable @escaping (Self.Failure) -> MappedFailure) -> some AsyncSequence { - AsyncMapErrorSequence(base: self, transform: transform) - } -} -``` +## Proposed solution + +A new extension and type will be added to transform the failure-types of AsyncSequences. ## Detailed design -The function iterates through the elements of an `AsyncSequence` within a do-catch block. If an error is caught, it calls the `transform` closure to convert the error into a new type and then throws it. +The method will be applied to all AsyncSequences via an extension with the function name of `mapError`. This is spiritually related to the `mapError` method on `Result` and similar in functionality to other frameworks' methods of the similar naming. This will not return an opaque result since the type needs to be refined for `Sendable`; in that the `AsyncMapERrorSequence` is only `Sendable` when the base `AsyncSequence` is `Sendable`. ```swift -struct AsyncMapErrorSequence: AsyncSequence { - - ... - - func makeAsyncIterator() -> Iterator { - Iterator( - base: base.makeAsyncIterator(), - transform: transform - ) - } +extension AsyncSequence { + public func mapError(_ transform: @Sendable @escaping (Failure) async -> MappedFailure) -> AsyncMapErrorSequence } -extension AsyncMapErrorSequence { - - struct Iterator: AsyncIteratorProtocol { +public struct AsyncMapErrorSequence: AsyncSequence { } - typealias Element = Base.Element +extension AsyncMapErrorSequence: Sendable where Base: Sendable { } - private var base: Base.AsyncIterator - - private let transform: @Sendable (Failure) -> MappedFailure +@available(*, unavailable) +extension AsyncMapErrorSequence.Iterator: Sendable {} +``` - init( - base: Base.AsyncIterator, - transform: @Sendable @escaping (Failure) -> MappedFailure - ) { - self.base = base - self.transform = transform - } +## Effect on API resilience - mutating func next() async throws(MappedFailure) -> Element? { - do { - return try await base.next(isolation: nil) - } catch { - throw transform(error) - } - } +This cannot be back-deployed to 1.0 since it has a base requirement for the associated `Failure` and requires typed throws. - mutating func next(isolation actor: isolated (any Actor)?) async throws(MappedFailure) -> Element? { - do { - return try await base.next(isolation: actor) - } catch { - throw transform(error) - } - } - } -} +## Naming -extension AsyncMapErrorSequence: Sendable where Base: Sendable, Base.Element: Sendable {} -``` +The naming follows to current method naming of the Combine [mapError](https://developer.apple.com/documentation/combine/publisher/maperror(_:)) method and similarly the name of the method on `Result` -## Naming +## Alternatives considered -The naming follows to current method naming of the Combine [mapError](https://developer.apple.com/documentation/combine/publisher/maperror(_:)) method. +It was initially considered that the return type would be opaque, however the only way to refine that as Sendable would be to have a disfavored overload; this ended up creating more ambiguity than it seemed worth. \ No newline at end of file diff --git a/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift b/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift index 75008f41..4b7953ee 100644 --- a/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift +++ b/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift @@ -11,95 +11,82 @@ //===----------------------------------------------------------------------===// #if compiler(>=6.0) +@available(AsyncAlgorithms 1.1, *) extension AsyncSequence { - /// Converts any failure into a new error. - /// - /// - Parameter transform: A closure that takes the failure as a parameter and returns a new error. - /// - Returns: An asynchronous sequence that maps the error thrown into the one produced by the transform closure. - /// - /// Use the ``mapError(_:)`` operator when you need to replace one error type with another. - @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) - public func mapError(_ transform: @Sendable @escaping (Self.Failure) -> MappedError) -> some AsyncSequence { - AsyncMapErrorSequence(base: self, transform: transform) - } - - /// Converts any failure into a new error. - /// - /// - Parameter transform: A closure that takes the failure as a parameter and returns a new error. - /// - Returns: An asynchronous sequence that maps the error thrown into the one produced by the transform closure. - /// - /// Use the ``mapError(_:)`` operator when you need to replace one error type with another. - @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) - public func mapError(_ transform: @Sendable @escaping (Self.Failure) -> MappedError) -> (some AsyncSequence & Sendable) where Self: Sendable, Self.Element: Sendable { - AsyncMapErrorSequence(base: self, transform: transform) - } + /// Converts any failure into a new error. + /// + /// - Parameter transform: A closure that takes the failure as a parameter and returns a new error. + /// - Returns: An asynchronous sequence that maps the error thrown into the one produced by the transform closure. + /// + /// Use the ``mapError(_:)`` operator when you need to replace one error type with another. + @available(AsyncAlgorithms 1.1, *) + public func mapError(_ transform: @Sendable @escaping (Failure) async -> MappedError) -> AsyncMapErrorSequence { + AsyncMapErrorSequence(base: self, transform: transform) + } } /// An asynchronous sequence that converts any failure into a new error. -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -fileprivate struct AsyncMapErrorSequence: AsyncSequence { +@available(AsyncAlgorithms 1.1, *) +public struct AsyncMapErrorSequence { + public typealias Element = Base.Element + public typealias Failure = MappedError + + private let base: Base + private let transform: @Sendable (Base.Failure) async -> MappedError + + init( + base: Base, + transform: @Sendable @escaping (Base.Failure) async -> MappedError + ) { + self.base = base + self.transform = transform + } +} - typealias AsyncIterator = Iterator - typealias Element = Base.Element - typealias Failure = Base.Failure +@available(AsyncAlgorithms 1.1, *) +extension AsyncMapErrorSequence: AsyncSequence { - private let base: Base - private let transform: @Sendable (Failure) -> MappedError + /// The iterator that produces elements of the map sequence. + public struct Iterator: AsyncIteratorProtocol { + public typealias Element = Base.Element + + private var base: Base.AsyncIterator + + private let transform: @Sendable (Base.Failure) async -> MappedError init( - base: Base, - transform: @Sendable @escaping (Failure) -> MappedError + base: Base.AsyncIterator, + transform: @Sendable @escaping (Base.Failure) async -> MappedError ) { - self.base = base - self.transform = transform + self.base = base + self.transform = transform } - func makeAsyncIterator() -> Iterator { - Iterator( - base: base.makeAsyncIterator(), - transform: transform - ) + public mutating func next() async throws(MappedError) -> Element? { + try await self.next(isolation: nil) } -} - -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -extension AsyncMapErrorSequence { - - /// The iterator that produces elements of the map sequence. - fileprivate struct Iterator: AsyncIteratorProtocol { - - typealias Element = Base.Element - - private var base: Base.AsyncIterator - - private let transform: @Sendable (Failure) -> MappedError - - init( - base: Base.AsyncIterator, - transform: @Sendable @escaping (Failure) -> MappedError - ) { - self.base = base - self.transform = transform - } - - mutating func next() async throws(MappedError) -> Element? { - try await self.next(isolation: nil) - } - mutating func next(isolation actor: isolated (any Actor)?) async throws(MappedError) -> Element? { - do { - return try await base.next(isolation: actor) - } catch { - throw transform(error) - } - } + public mutating func next(isolation actor: isolated (any Actor)?) async throws(MappedError) -> Element? { + do { + return try await base.next(isolation: actor) + } catch { + throw await transform(error) + } } + } + + public func makeAsyncIterator() -> Iterator { + Iterator( + base: base.makeAsyncIterator(), + transform: transform + ) + } } -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -extension AsyncMapErrorSequence: Sendable where Base: Sendable, Base.Element: Sendable {} +@available(AsyncAlgorithms 1.1, *) +extension AsyncMapErrorSequence: Sendable where Base: Sendable { } @available(*, unavailable) -extension AsyncMapErrorSequence.Iterator: Sendable {} +extension AsyncMapErrorSequence.Iterator: Sendable { } #endif diff --git a/Tests/AsyncAlgorithmsTests/TestMapError.swift b/Tests/AsyncAlgorithmsTests/TestMapError.swift index afe90e13..9903e140 100644 --- a/Tests/AsyncAlgorithmsTests/TestMapError.swift +++ b/Tests/AsyncAlgorithmsTests/TestMapError.swift @@ -5,101 +5,101 @@ import XCTest @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) final class TestMapError: XCTestCase { - func test_mapError() async throws { - let array: [Any] = [1, 2, 3, MyAwesomeError.originalError, 4, 5, 6] - let sequence = array.async - .map { - if let error = $0 as? Error { - throw error - } else { - $0 as! Int - } - } - .mapError { _ in - MyAwesomeError.mappedError - } - - var results: [Int] = [] - - do { - for try await number in sequence { - results.append(number) - } - XCTFail("sequence should throw") - } catch { - XCTAssertEqual(error, .mappedError) + func test_mapError() async throws { + let array: [Any] = [1, 2, 3, MyAwesomeError.originalError, 4, 5, 6] + let sequence = array.async + .map { + if let error = $0 as? Error { + throw error + } else { + $0 as! Int } + } + .mapError { _ in + MyAwesomeError.mappedError + } - XCTAssertEqual(results, [1, 2, 3]) + var results: [Int] = [] + + do { + for try await number in sequence { + results.append(number) + } + XCTFail("sequence should throw") + } catch { + XCTAssertEqual(error, .mappedError) } - func test_mapError_cancellation() async throws { - let value = "test" - let source = Indefinite(value: value).async - let sequence = source - .map { - if $0 == "just to trick compiler that this may throw" { - throw MyAwesomeError.originalError - } else { - $0 - } - } - .mapError { _ in - MyAwesomeError.mappedError - } + XCTAssertEqual(results, [1, 2, 3]) + } + + func test_mapError_cancellation() async throws { + let value = "test" + let source = Indefinite(value: value).async + let sequence = source + .map { + if $0 == "just to trick compiler that this may throw" { + throw MyAwesomeError.originalError + } else { + $0 + } + } + .mapError { _ in + MyAwesomeError.mappedError + } - let finished = expectation(description: "finished") - let iterated = expectation(description: "iterated") + let finished = expectation(description: "finished") + let iterated = expectation(description: "iterated") - let task = Task { - var firstIteration = false - for try await el in sequence { - XCTAssertEqual(el, value) + let task = Task { + var firstIteration = false + for try await el in sequence { + XCTAssertEqual(el, value) - if !firstIteration { - firstIteration = true - iterated.fulfill() - } - } - finished.fulfill() + if !firstIteration { + firstIteration = true + iterated.fulfill() } - - // ensure the other task actually starts - await fulfillment(of: [iterated], timeout: 1.0) - // cancellation should ensure the loop finishes - // without regards to the remaining underlying sequence - task.cancel() - await fulfillment(of: [finished], timeout: 1.0) + } + finished.fulfill() } - func test_mapError_empty() async throws { - let array: [String] = [] - let sequence = array.async - .map { - if $0 == "just to trick compiler that this may throw" { - throw MyAwesomeError.originalError - } else { - $0 - } - } - .mapError { _ in - MyAwesomeError.mappedError - } + // ensure the other task actually starts + await fulfillment(of: [iterated], timeout: 1.0) + // cancellation should ensure the loop finishes + // without regards to the remaining underlying sequence + task.cancel() + await fulfillment(of: [finished], timeout: 1.0) + } - var results: [String] = [] - for try await value in sequence { - results.append(value) + func test_mapError_empty() async throws { + let array: [String] = [] + let sequence = array.async + .map { + if $0 == "just to trick compiler that this may throw" { + throw MyAwesomeError.originalError + } else { + $0 } - XCTAssert(results.isEmpty) + } + .mapError { _ in + MyAwesomeError.mappedError + } + + var results: [String] = [] + for try await value in sequence { + results.append(value) } + XCTAssert(results.isEmpty) + } } @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) private extension TestMapError { - enum MyAwesomeError: Error { - case originalError - case mappedError - } + enum MyAwesomeError: Error { + case originalError + case mappedError + } } #endif From e59c3a280cfed6bc60f1540ee436485d271f9fb7 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Thu, 11 Sep 2025 13:45:55 -0700 Subject: [PATCH 3/5] Foratting and preambles --- .../AsyncMapErrorSequence.swift | 15 ++++++++------- Tests/AsyncAlgorithmsTests/TestMapError.swift | 18 +++++++++++++++--- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift b/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift index 4b7953ee..ea99efd1 100644 --- a/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift +++ b/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift @@ -1,12 +1,11 @@ //===----------------------------------------------------------------------===// // -// This source file is part of the Swift.org open source project +// This source file is part of the Swift Async Algorithms open source project // -// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Copyright (c) 2022 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// @@ -21,7 +20,9 @@ extension AsyncSequence { /// /// Use the ``mapError(_:)`` operator when you need to replace one error type with another. @available(AsyncAlgorithms 1.1, *) - public func mapError(_ transform: @Sendable @escaping (Failure) async -> MappedError) -> AsyncMapErrorSequence { + public func mapError( + _ transform: @Sendable @escaping (Failure) async -> MappedError + ) -> AsyncMapErrorSequence { AsyncMapErrorSequence(base: self, transform: transform) } } @@ -45,7 +46,7 @@ public struct AsyncMapErrorSequence { } @available(AsyncAlgorithms 1.1, *) -extension AsyncMapErrorSequence: AsyncSequence { +extension AsyncMapErrorSequence: AsyncSequence { /// The iterator that produces elements of the map sequence. public struct Iterator: AsyncIteratorProtocol { @@ -85,8 +86,8 @@ extension AsyncMapErrorSequence: AsyncSequence { } @available(AsyncAlgorithms 1.1, *) -extension AsyncMapErrorSequence: Sendable where Base: Sendable { } +extension AsyncMapErrorSequence: Sendable where Base: Sendable {} @available(*, unavailable) -extension AsyncMapErrorSequence.Iterator: Sendable { } +extension AsyncMapErrorSequence.Iterator: Sendable {} #endif diff --git a/Tests/AsyncAlgorithmsTests/TestMapError.swift b/Tests/AsyncAlgorithmsTests/TestMapError.swift index 9903e140..717c6b3b 100644 --- a/Tests/AsyncAlgorithmsTests/TestMapError.swift +++ b/Tests/AsyncAlgorithmsTests/TestMapError.swift @@ -1,3 +1,14 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Async Algorithms open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + import AsyncAlgorithms import XCTest @@ -36,7 +47,8 @@ final class TestMapError: XCTestCase { func test_mapError_cancellation() async throws { let value = "test" let source = Indefinite(value: value).async - let sequence = source + let sequence = + source .map { if $0 == "just to trick compiler that this may throw" { throw MyAwesomeError.originalError @@ -95,9 +107,9 @@ final class TestMapError: XCTestCase { } @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -private extension TestMapError { +extension TestMapError { - enum MyAwesomeError: Error { + fileprivate enum MyAwesomeError: Error { case originalError case mappedError } From 9ad2465df54d887b1d60657d1864ad1170c763a7 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Fri, 12 Sep 2025 08:51:29 -0700 Subject: [PATCH 4/5] Update Copyright date for error sequence Co-authored-by: Clive --- Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift b/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift index ea99efd1..b4a8b91f 100644 --- a/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift +++ b/Sources/AsyncAlgorithms/AsyncMapErrorSequence.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Async Algorithms open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information From 980e5f78262905a829bc7d849d71412c521bc55a Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Fri, 12 Sep 2025 08:51:48 -0700 Subject: [PATCH 5/5] Update copyright year for mapError tests Co-authored-by: Clive --- Tests/AsyncAlgorithmsTests/TestMapError.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/AsyncAlgorithmsTests/TestMapError.swift b/Tests/AsyncAlgorithmsTests/TestMapError.swift index 717c6b3b..ef3a0ddb 100644 --- a/Tests/AsyncAlgorithmsTests/TestMapError.swift +++ b/Tests/AsyncAlgorithmsTests/TestMapError.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift Async Algorithms open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Copyright (c) 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information