Last active
October 7, 2022 21:08
-
-
Save swhitty/9aa7f62bef03510874a32ec210ec4221 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// .print("🐡") Similar to the Combine variant, but for AsyncSequence. | |
/// | |
/// ``` | |
/// 🐡: makeAsyncIterator(): AsyncMapSequence<String> | |
/// 🐡: receive value: (1) | |
/// 🐡: receive value: (2) | |
/// 🐡: finished: AsyncMapSequence<String> | |
/// ``` | |
/// | |
public extension AsyncSequence { | |
/// Prints log messages for all iteration events. | |
/// - Parameters: | |
/// - prefix: A string — which defaults to empty, with which to prefix all log messages. | |
/// - stream: A stream for text output that receives messages. Defaults to console. | |
/// - Returns: A sequence that prints log messages for all iteration events. | |
func print(_ prefix: String = "", to stream: TextOutputStream? = nil) -> AsyncPrintSequence<Self> { | |
AsyncPrintSequence(upstream: self, prefix: prefix, stream: stream) | |
} | |
} | |
/// A sequence that prints log messages for all iteration events, optionally prefixed with a given string. | |
public struct AsyncPrintSequence<Upstream>: AsyncSequence where Upstream: AsyncSequence { | |
public typealias Element = Upstream.Element | |
private let upstream: Upstream | |
private let logger: Logger | |
/// Creates a sequence that prints log messages for all iteration events. | |
/// - Parameters: | |
/// - upstream: The sequence from which this seqence receives elements. | |
/// - prefix: A string with which to prefix all log messages. | |
/// - stream: A stream for text output that receives messages. Defaults to console. | |
public init(upstream: Upstream, prefix: String, stream: TextOutputStream? = nil) { | |
self.upstream = upstream | |
self.logger = Logger(prefix: prefix, stream: stream) | |
} | |
public func makeAsyncIterator() -> Iterator<Upstream.AsyncIterator> { | |
Iterator(upstream.makeAsyncIterator(), logger: logger) | |
} | |
public struct Iterator<Inner>: AsyncIteratorProtocol where Inner: AsyncIteratorProtocol, Element == Inner.Element { | |
private var it: Inner | |
private let logger: Logger | |
fileprivate init(_ it: Inner, logger: Logger) { | |
self.it = it | |
self.logger = logger | |
logger.logMakeIterator() | |
} | |
public mutating func next() async throws -> Inner.Element? { | |
do { | |
let element = try await it.next() | |
if let element = element { | |
logger.logValue(element) | |
} else { | |
logger.logFinished() | |
} | |
return element | |
} catch { | |
logger.logError(error) | |
throw error | |
} | |
} | |
} | |
} | |
private extension AsyncPrintSequence { | |
struct Logger { | |
let prefix: String | |
let stream: TextOutputStream? | |
func log(_ message: String) { | |
let message = prefix.isEmpty ? message : "\(prefix): \(message)" | |
if var stream = stream.map(AnyStream.init) { | |
Swift.print(message, terminator: "\n", to: &stream) | |
} else { | |
Swift.print(message) | |
} | |
} | |
private func makeIdentifier() -> String { | |
let elementName = String(describing: Upstream.Element.self) | |
let upstreamName = String(describing: Upstream.self).prefix { $0 != "<" } | |
return "\(upstreamName)<\(elementName)>" | |
} | |
func logMakeIterator() { | |
log("makeAsyncIterator(): \(makeIdentifier())") | |
} | |
func logValue(_ value: Element) { | |
log("receive value: (\(String(describing: value)))") | |
} | |
func logError(_ error: Error) { | |
log("finished error: (\(String(describing: error)))") | |
} | |
func logFinished() { | |
log("finished: \(makeIdentifier())") | |
} | |
private struct AnyStream: TextOutputStream { | |
var inner: TextOutputStream | |
mutating func write(_ string: String) { | |
inner.write(string) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment