Skip to content

Commit

Permalink
feat: refactor iterators (#41)
Browse files Browse the repository at this point in the history
* fix: check for cancellation

* feat: extract iterator into own struct
  • Loading branch information
reddavis authored Sep 22, 2022
1 parent dc59c38 commit cf55cd1
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 46 deletions.
1 change: 1 addition & 0 deletions Asynchrone/Source/Extensions/AsyncSequence+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ extension AsyncSequence {
Task(priority: priority) {
for try await element in self {
await receiveValue(element)
try Task.checkCancellation()
}
}
}
Expand Down
70 changes: 39 additions & 31 deletions Asynchrone/Source/Sequences/RemoveDuplicatesAsyncSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ public struct RemoveDuplicatesAsyncSequence<Base: AsyncSequence>: AsyncSequence
// Private
private let base: Base
private let predicate: Predicate
private var iterator: Base.AsyncIterator
private var previousElement: Base.Element?

// MARK: Initialization

Expand All @@ -45,55 +43,65 @@ public struct RemoveDuplicatesAsyncSequence<Base: AsyncSequence>: AsyncSequence
) {
self.base = base
self.predicate = predicate
self.iterator = base.makeAsyncIterator()
}

// MARK: AsyncSequence

/// Creates an async iterator that emits elements of this async sequence.
/// - Returns: An instance that conforms to `AsyncIteratorProtocol`.
public func makeAsyncIterator() -> Self {
.init(base: self.base, predicate: self.predicate)
public func makeAsyncIterator() -> Iterator {
Iterator(base: self.base, predicate: self.predicate)
}
}

extension RemoveDuplicatesAsyncSequence: Sendable
where
Base: Sendable,
Base.AsyncIterator: Sendable,
Base.Element: Sendable {}
Base: Sendable {}

// MARK: AsyncIteratorProtocol
// MARK: Iterator

extension RemoveDuplicatesAsyncSequence: AsyncIteratorProtocol {
/// Produces the next element in the sequence.
///
/// Continues to call `next()` on it's base iterator and discard the
/// results if the predicate returns true.
///
/// The first element of the sequence is always returned.
///
/// If the base iterator returns `nil`, indicating the end of the sequence, this
/// iterator returns `nil`.
/// - Returns: The next element or `nil` if the end of the sequence is reached.
public mutating func next() async rethrows -> Element? {
let element = try await self.iterator.next()
let previousElement = self.previousElement
extension RemoveDuplicatesAsyncSequence {
public struct Iterator: AsyncIteratorProtocol {
private let predicate: Predicate
private var iterator: Base.AsyncIterator
private var previousElement: Base.Element?

// Update previous element
self.previousElement = element
// MARK: Initialization

init(
base: Base,
predicate: @escaping Predicate
) {
self.iterator = base.makeAsyncIterator()
self.predicate = predicate
}

guard let unwrappedElement = element,
let unwrappedPreviousElement = previousElement else { return element }
// MARK: AsyncIteratorProtocol

if self.predicate(unwrappedPreviousElement, unwrappedElement) {
return try await self.next()
} else {
return element
public mutating func next() async rethrows -> Element? {
let element = try await self.iterator.next()
let previousElement = self.previousElement

// Update previous element
self.previousElement = element

guard let unwrappedElement = element,
let unwrappedPreviousElement = previousElement else { return element }

if self.predicate(unwrappedPreviousElement, unwrappedElement) {
return try await self.next()
} else {
return element
}
}
}
}

extension RemoveDuplicatesAsyncSequence.Iterator: Sendable
where
Base.AsyncIterator: Sendable,
Base.Element: Sendable {}

// MARK: Remove duplicates

extension AsyncSequence where Element: Equatable {
Expand Down
47 changes: 32 additions & 15 deletions Asynchrone/Source/Sequences/ReplaceErrorAsyncSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ public struct ReplaceErrorAsyncSequence<Base: AsyncSequence>: AsyncSequence {
// Private
private let base: Base
private let replacement: Element
private var iterator: Base.AsyncIterator

// MARK: Initialization

Expand All @@ -31,38 +30,56 @@ public struct ReplaceErrorAsyncSequence<Base: AsyncSequence>: AsyncSequence {
public init(base: Base, output: Element) {
self.base = base
self.replacement = output
self.iterator = base.makeAsyncIterator()
}

// MARK: AsyncSequence

/// Creates an async iterator that emits elements of this async sequence.
/// - Returns: An instance that conforms to `AsyncIteratorProtocol`.
public func makeAsyncIterator() -> Self {
.init(base: self.base, output: self.replacement)
public func makeAsyncIterator() -> Iterator {
Iterator(base: self.base, output: self.replacement)
}
}

extension ReplaceErrorAsyncSequence: Sendable
where
Base: Sendable,
Base.Element: Sendable,
Base.AsyncIterator: Sendable {}
Base.Element: Sendable {}

// MARK: AsyncIteratorProtocol
// MARK: Iterator

extension ReplaceErrorAsyncSequence: AsyncIteratorProtocol {
/// Produces the next element in the sequence.
/// - Returns: The next element or `nil` if the end of the sequence is reached.
public mutating func next() async -> Element? {
do {
return try await self.iterator.next()
} catch {
return self.replacement
extension ReplaceErrorAsyncSequence {
public struct Iterator: AsyncIteratorProtocol {
private let replacement: Element
private var iterator: Base.AsyncIterator

// MARK: Initialization

init(
base: Base,
output: Element
) {
self.iterator = base.makeAsyncIterator()
self.replacement = output
}

// MARK: AsyncIteratorProtocol

public mutating func next() async -> Element? {
do {
return try await self.iterator.next()
} catch {
return self.replacement
}
}
}
}

extension ReplaceErrorAsyncSequence.Iterator: Sendable
where
Base.AsyncIterator: Sendable,
Base.Element: Sendable {}

// MARK: Replace error

extension AsyncSequence {
Expand Down

0 comments on commit cf55cd1

Please sign in to comment.