//===----------------------------------------------------------------------===// // // 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 { /// Omits elements from the base sequence until a given error-throwing closure /// returns false, after which it passes through all remaining elements. /// /// Use `drop(while:)` to omit elements from an asynchronous sequence until /// the element received meets a condition you specify. If the closure you /// provide throws an error, the sequence produces no elements and throws /// the error instead. /// /// In this example, an asynchronous sequence called `Counter` produces `Int` /// values from `1` to `10`. The predicate passed to the `drop(while:)` /// method throws an error if it encounters an even number, and otherwise /// returns `true` while it receives elements less than `5`. Because the /// predicate throws when it receives `2` from the base sequence, this example /// throws without ever printing anything. /// /// do { /// let stream = Counter(howHigh: 10) /// .drop { /// if $0 % 2 == 0 { /// throw EvenError() /// } /// return $0 < 5 /// } /// for try await number in stream { /// print(number) /// } /// } catch { /// print(error) /// } /// // Prints "EvenError()" /// /// After the predicate returns `false`, the sequence never executes it again, /// and from then on the sequence passes through elements from its underlying /// sequence. A predicate that throws an error also never executes again. /// /// - Parameter predicate: An error-throwing closure that takes an element as /// a parameter and returns a Boolean value indicating whether to drop the /// element from the modified sequence. /// - Returns: An asynchronous sequence that skips over values until the /// provided closure returns `false` or throws an error. @preconcurrency @inlinable public __consuming func drop( while predicate: @Sendable @escaping (Element) async throws -> Bool ) -> AsyncThrowingDropWhileSequence { AsyncThrowingDropWhileSequence(self, predicate: predicate) } } /// An asynchronous sequence which omits elements from the base sequence until a /// given error-throwing closure returns false, after which it passes through /// all remaining elements. @available(SwiftStdlib 5.1, *) public struct AsyncThrowingDropWhileSequence { @usableFromInline let base: Base @usableFromInline let predicate: (Base.Element) async throws -> Bool @usableFromInline init( _ base: Base, predicate: @escaping (Base.Element) async throws -> Bool ) { self.base = base self.predicate = predicate } } @available(SwiftStdlib 5.1, *) extension AsyncThrowingDropWhileSequence: AsyncSequence { /// The type of element produced by this asynchronous sequence. /// /// The drop-while sequence produces whatever type of element its base /// sequence produces. public typealias Element = Base.Element /// The type of element produced by this asynchronous sequence. /// /// The drop-while sequence produces errors from either the base /// sequence or the filtering 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 drop-while sequence. public struct Iterator: AsyncIteratorProtocol { @usableFromInline var baseIterator: Base.AsyncIterator @usableFromInline let predicate: (Base.Element) async throws -> Bool @usableFromInline var finished = false @usableFromInline var doneDropping = false @usableFromInline init( _ baseIterator: Base.AsyncIterator, predicate: @escaping (Base.Element) async throws -> Bool ) { self.baseIterator = baseIterator self.predicate = predicate } /// Produces the next element in the drop-while sequence. /// /// This iterator calls `next()` on its base iterator and evaluates the /// result with the `predicate` closure. As long as the predicate returns /// `true`, this method returns `nil`. After the predicate returns `false`, /// for a value received from the base iterator, this method returns that /// value. After that, the iterator returns values received from its /// base iterator as-is, and never executes the predicate closure again. /// If calling the closure throws an error, the sequence ends and `next()` /// rethrows the error. @inlinable public mutating func next() async throws -> Base.Element? { while !finished && !doneDropping { guard let element = try await baseIterator.next() else { return nil } do { if try await predicate(element) == false { doneDropping = true return element } } catch { finished = true throw error } } guard !finished else { return nil } return try await baseIterator.next() } /// Produces the next element in the drop-while sequence. /// /// This iterator calls `next(isolation:)` on its base iterator and /// evaluates the result with the `predicate` closure. As long as the /// predicate returns `true`, this method returns `nil`. After the predicate /// returns `false`, for a value received from the base iterator, this /// method returns that value. After that, the iterator returns values /// received from its base iterator as-is, and never executes the predicate /// closure again. If calling the closure throws an error, the sequence /// ends and `next(isolation:)` rethrows the error. @available(SwiftStdlib 6.0, *) @inlinable public mutating func next(isolation actor: isolated (any Actor)?) async throws(Failure) -> Base.Element? { while !finished && !doneDropping { guard let element = try await baseIterator.next(isolation: actor) else { return nil } do { if try await predicate(element) == false { doneDropping = true return element } } catch { finished = true throw error } } guard !finished else { return nil } return try await baseIterator.next(isolation: actor) } } @inlinable public __consuming func makeAsyncIterator() -> Iterator { return Iterator(base.makeAsyncIterator(), predicate: predicate) } } @available(SwiftStdlib 5.1, *) extension AsyncThrowingDropWhileSequence: @unchecked Sendable where Base: Sendable, Base.Element: Sendable { } @available(SwiftStdlib 5.1, *) extension AsyncThrowingDropWhileSequence.Iterator: @unchecked Sendable where Base.AsyncIterator: Sendable, Base.Element: Sendable { }