//===----------------------------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2021 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 // //===----------------------------------------------------------------------===// import Swift @available(SwiftStdlib 5.1, *) extension AsyncSequence { /// Creates an asynchronous sequence that maps an error-throwing closure over /// the base sequence’s elements, omitting results that don't return a value. /// /// Use the `compactMap(_:)` method to transform every element received from /// a base asynchronous sequence, while also discarding any `nil` results /// from the closure. Typically, you use this to transform from one type of /// element to another. /// /// In this example, an asynchronous sequence called `Counter` produces `Int` /// values from `1` to `5`. The closure provided to the `compactMap(_:)` /// method takes each `Int` and looks up a corresponding `String` from a /// `romanNumeralDict` dictionary. Since there is no key for `4`, the closure /// returns `nil` in this case, which `compactMap(_:)` omits from the /// transformed asynchronous sequence. When the value is `5`, the closure /// throws `MyError`, terminating the sequence. /// /// let romanNumeralDict: [Int: String] = /// [1: "I", 2: "II", 3: "III", 5: "V"] /// /// do { /// let stream = Counter(howHigh: 5) /// .compactMap { (value) throws -> String? in /// if value == 5 { /// throw MyError() /// } /// return romanNumeralDict[value] /// } /// for try await numeral in stream { /// print(numeral, terminator: " ") /// } /// } catch { /// print("Error: \(error)") /// } /// // Prints "I II III Error: MyError() " /// /// - Parameter transform: An error-throwing mapping closure. `transform` /// accepts an element of this sequence as its parameter and returns a /// transformed value of the same or of a different type. If `transform` /// throws an error, the sequence ends. /// - Returns: An asynchronous sequence that contains, in order, the /// non-`nil` elements produced by the `transform` closure. The sequence /// ends either when the base sequence ends or when `transform` throws an /// error. @preconcurrency @inlinable public __consuming func compactMap( _ transform: @Sendable @escaping (Element) async throws -> ElementOfResult? ) -> AsyncThrowingCompactMapSequence { return AsyncThrowingCompactMapSequence(self, transform: transform) } } /// An asynchronous sequence that maps an error-throwing closure over the base /// sequence’s elements, omitting results that don't return a value. @available(SwiftStdlib 5.1, *) public struct AsyncThrowingCompactMapSequence { @usableFromInline let base: Base @usableFromInline let transform: (Base.Element) async throws -> ElementOfResult? @usableFromInline init( _ base: Base, transform: @escaping (Base.Element) async throws -> ElementOfResult? ) { self.base = base self.transform = transform } } @available(SwiftStdlib 5.1, *) extension AsyncThrowingCompactMapSequence: AsyncSequence { /// The type of element produced by this asynchronous sequence. /// /// The compact map sequence produces whatever type of element its /// transforming closure produces. public typealias Element = ElementOfResult /// The type of element produced by this asynchronous sequence. /// /// The compact map sequence produces errors from either the base /// sequence or the transforming closure. public typealias Failure = any Error /// The type of iterator that produces elements of the sequence. public typealias AsyncIterator = Iterator /// The iterator that produces elements of the compact map sequence. public struct Iterator: AsyncIteratorProtocol { public typealias Element = ElementOfResult @usableFromInline var baseIterator: Base.AsyncIterator @usableFromInline let transform: (Base.Element) async throws -> ElementOfResult? @usableFromInline var finished = false @usableFromInline init( _ baseIterator: Base.AsyncIterator, transform: @escaping (Base.Element) async throws -> ElementOfResult? ) { self.baseIterator = baseIterator self.transform = transform } /// Produces the next element in the compact map sequence. /// /// This iterator calls `next()` on its base iterator; if this call returns /// `nil`, `next()` returns `nil`. Otherwise, `next()` calls the /// transforming closure on the received element, returning it if the /// transform returns a non-`nil` value. If the transform returns `nil`, /// this method continues to wait for further elements until it gets one /// that transforms to a non-`nil` value. If calling the closure throws an /// error, the sequence ends and `next()` rethrows the error. @inlinable public mutating func next() async throws -> ElementOfResult? { while !finished { guard let element = try await baseIterator.next() else { finished = true return nil } do { if let transformed = try await transform(element) { return transformed } } catch { finished = true throw error } } return nil } /// Produces the next element in the compact map sequence. /// /// This iterator calls `next()` on its base iterator; if this call /// returns `nil`, `next()` returns `nil`. Otherwise, `next()` /// calls the transforming closure on the received element, returning it if /// the transform returns a non-`nil` value. If the transform returns `nil`, /// this method continues to wait for further elements until it gets one /// that transforms to a non-`nil` value. If calling the closure throws an /// error, the sequence ends and `next()` rethrows the error. @available(SwiftStdlib 6.0, *) @inlinable public mutating func next(isolation actor: isolated (any Actor)?) async throws(Failure) -> ElementOfResult? { while !finished { guard let element = try await baseIterator.next(isolation: actor) else { finished = true return nil } do { if let transformed = try await transform(element) { return transformed } } catch { finished = true throw error } } return nil } } @inlinable public __consuming func makeAsyncIterator() -> Iterator { return Iterator(base.makeAsyncIterator(), transform: transform) } } @available(SwiftStdlib 5.1, *) extension AsyncThrowingCompactMapSequence: @unchecked Sendable where Base: Sendable, Base.Element: Sendable { } @available(SwiftStdlib 5.1, *) extension AsyncThrowingCompactMapSequence.Iterator: @unchecked Sendable where Base.AsyncIterator: Sendable, Base.Element: Sendable { }