|
| 1 | +// |
| 2 | +// AsyncReadSequence.swift |
| 3 | +// AsyncSequenceReader |
| 4 | +// |
| 5 | +// Created by Dimitri Bouniol on 2021-11-17. |
| 6 | +// Copyright © 2021 Mochi Development, Inc. All rights reserved. |
| 7 | +// |
| 8 | + |
| 9 | +#if compiler(>=5.5) && canImport(_Concurrency) |
| 10 | + |
| 11 | +/// An AsyncSequence subtype suitable for reading an existing iterator in place. |
| 12 | +/// |
| 13 | +/// Note that to conform to this protocol, your type must be a reference type. After iterating, you'll also likely want to copy the base iterator back into your starting iterator, as shown in ``AsyncIteratorProtocol.transform(with:readSequenceFactory:)``. |
| 14 | +public protocol AsyncReadSequence : AsyncSequence, AnyObject { |
| 15 | + associatedtype BaseIterator : AsyncIteratorProtocol where BaseIterator.Element == Element |
| 16 | + |
| 17 | + @inlinable |
| 18 | + var baseIterator: AsyncBufferedIterator<BaseIterator> { get } |
| 19 | +} |
| 20 | + |
| 21 | +extension AsyncIteratorProtocol { |
| 22 | + /// Transform the receiving iterator using the specified sequence transformer and configured read sequence. |
| 23 | + /// |
| 24 | + /// - Note: Iterating over the read sequence multiple times will result in undefined behavior. |
| 25 | + /// |
| 26 | + /// - Parameter sequenceTransform: A transformation that accepts a sequence that can be read from, or stopped prematurely by returning `nil`. The receiving iterator will have moved forward by the same amount of items consumed within `sequenceTransform`. |
| 27 | + /// - Parameter readSequenceFactory: A factory to create a suitable ``AsyncReadSequence`` that will determine the logical bounds of the transformation within the receiving iterator. |
| 28 | + /// - Returns: A transformed value read from the iterator, or `nil` if there were no values left to read. |
| 29 | + public mutating func transform<Transformed, ReadSequence: AsyncReadSequence>( |
| 30 | + with sequenceTransform: (ReadSequence) async throws -> Transformed, |
| 31 | + readSequenceFactory: (inout AsyncBufferedIterator<Self>) -> ReadSequence |
| 32 | + ) async rethrows -> Transformed? where ReadSequence.BaseIterator == Self { |
| 33 | + var results: Transformed? = nil |
| 34 | + var wrappedIterator = AsyncBufferedIterator(self) |
| 35 | + if try await wrappedIterator.hasMoreData() { |
| 36 | + let readSequence = readSequenceFactory(&wrappedIterator) |
| 37 | + results = try await sequenceTransform(readSequence) |
| 38 | + wrappedIterator = readSequence.baseIterator |
| 39 | + } |
| 40 | + self = wrappedIterator.baseIterator |
| 41 | + |
| 42 | + return results |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +extension AsyncBufferedIterator { |
| 47 | + /// Transform the receiving iterator using the specified sequence transformer and configured read sequence. |
| 48 | + /// |
| 49 | + /// - Note: Iterating over the read sequence multiple times will result in undefined behavior. |
| 50 | + /// |
| 51 | + /// - Parameter sequenceTransform: A transformation that accepts a sequence that can be read from, or stopped prematurely by returning `nil`. The receiving iterator will have moved forward by the same amount of items consumed within `sequenceTransform`. |
| 52 | + /// - Parameter readSequenceFactory: A factory to create a suitable ``AsyncReadSequence`` that will determine the logical bounds of the transformation within the receiving iterator. |
| 53 | + /// - Returns: A transformed value read from the iterator, or `nil` if there were no values left to read. |
| 54 | + public mutating func transform<Transformed, ReadSequence: AsyncReadSequence>( |
| 55 | + with sequenceTransform: (ReadSequence) async throws -> Transformed, |
| 56 | + readSequenceFactory: (inout Self) -> ReadSequence |
| 57 | + ) async rethrows -> Transformed? where ReadSequence.BaseIterator == BaseIterator { |
| 58 | + |
| 59 | + var results: Transformed? = nil |
| 60 | + if try await self.hasMoreData() { |
| 61 | + let readSequence = readSequenceFactory(&self) |
| 62 | + results = try await sequenceTransform(readSequence) |
| 63 | + self = readSequence.baseIterator |
| 64 | + } |
| 65 | + |
| 66 | + return results |
| 67 | + } |
| 68 | +} |
| 69 | + |
| 70 | +#endif |
0 commit comments