forked from sideeffect-io/AsyncExtensions
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAsyncScanSequence.swift
92 lines (83 loc) · 2.87 KB
/
AsyncScanSequence.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//
// AsyncSequence+Scan.swift
//
//
// Created by Thibault Wittemberg on 31/12/2021.
//
public extension AsyncSequence {
/// Transforms elements from the upstream async sequence by providing the current element to a closure
/// along with the last value returned by the closure.
///
/// ```
/// let sourceSequence = AsyncLazySequence([1, 2, 3, 4, 5])
/// let scannedSequence = sourceSequence.scan("") { accumulator, element in
/// return accumulator + "\(element)"
/// }
/// for try await element in scannedSequence {
/// print(element)
/// }
///
/// // will print:
/// "1"
/// "12"
/// "123"
/// "1234"
/// "12345"
/// ```
///
/// - Parameters:
/// - initialResult: The initial value of the result.
/// - nextPartialResult: The closure to execute on each element of the source sequence.
/// - Returns: The async sequence of all the partial results.
func scan<Output>(
_ initialResult: Output,
_ nextPartialResult: @Sendable @escaping (Output, Element) async -> Output
) -> AsyncScanSequence<Self, Output> {
AsyncScanSequence(self, initialResult: initialResult, nextPartialResult: nextPartialResult)
}
}
public struct AsyncScanSequence<Base: AsyncSequence, Output>: AsyncSequence {
public typealias Element = Output
public typealias AsyncIterator = Iterator
var base: Base
var initialResult: Output
let nextPartialResult: @Sendable (Output, Base.Element) async -> Output
public init(
_ base: Base,
initialResult: Output,
nextPartialResult: @Sendable @escaping (Output, Base.Element) async -> Output
) {
self.base = base
self.initialResult = initialResult
self.nextPartialResult = nextPartialResult
}
public func makeAsyncIterator() -> AsyncIterator {
Iterator(
base: self.base.makeAsyncIterator(),
initialResult: self.initialResult,
nextPartialResult: self.nextPartialResult
)
}
public struct Iterator: AsyncIteratorProtocol {
var base: Base.AsyncIterator
var currentValue: Output
let nextPartialResult: @Sendable (Output, Base.Element) async -> Output
public init(
base: Base.AsyncIterator,
initialResult: Output,
nextPartialResult: @Sendable @escaping (Output, Base.Element) async -> Output
) {
self.base = base
self.currentValue = initialResult
self.nextPartialResult = nextPartialResult
}
public mutating func next() async rethrows -> Output? {
let nextUpstreamValue = try await self.base.next()
guard let nonNilNextUpstreamValue = nextUpstreamValue else { return nil }
self.currentValue = await self.nextPartialResult(self.currentValue, nonNilNextUpstreamValue)
return self.currentValue
}
}
}
extension AsyncScanSequence: Sendable where Base: Sendable, Output: Sendable {}
extension AsyncScanSequence.Iterator: Sendable where Base.AsyncIterator: Sendable, Output: Sendable {}