From 9e4136ec8a349ef1968b6a532e8d9ef69ce5e947 Mon Sep 17 00:00:00 2001 From: RuiyangSun Date: Sun, 21 Apr 2024 21:36:54 +0800 Subject: [PATCH 1/2] chore(package): add platform --- Package.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Package.swift b/Package.swift index 6a8af2c..7738e92 100644 --- a/Package.swift +++ b/Package.swift @@ -5,6 +5,14 @@ import PackageDescription let package = Package( name: "SyncStream", + platforms: [ + .macOS(.v13), + .iOS(.v16), + .watchOS(.v9), + .visionOS(.v1), + .macCatalyst(.v16), + .tvOS(.v16), + ], products: [ // Products define the executables and libraries a package produces, making them visible to other packages. .library( From 488b0dc8f9e16d5181a44bcc5bd54211abeed0cf Mon Sep 17 00:00:00 2001 From: RuiyangSun Date: Sun, 21 Apr 2024 23:19:57 +0800 Subject: [PATCH 2/2] refactor(BidirectionalStream): build support throws --- .swiftlint.yaml | 2 +- .../SyncStream/BidirectionalAsyncStream.swift | 45 ++++++++++++ .../SyncStream/BidirectionalSyncStream.swift | 72 ++++++++++++++++++- 3 files changed, 117 insertions(+), 2 deletions(-) diff --git a/.swiftlint.yaml b/.swiftlint.yaml index 11e3cc9..2ed1063 100644 --- a/.swiftlint.yaml +++ b/.swiftlint.yaml @@ -56,7 +56,7 @@ opt_in_rules: # some rules are turned off by default, so you need to opt-in - joined_default_parameter - last_where - legacy_multiple - - legacy_objc_type + # - legacy_objc_type - let_var_whitespace - literal_expression_end_indentation - local_doc_comment diff --git a/Sources/SyncStream/BidirectionalAsyncStream.swift b/Sources/SyncStream/BidirectionalAsyncStream.swift index b2f68dd..74b6bbc 100644 --- a/Sources/SyncStream/BidirectionalAsyncStream.swift +++ b/Sources/SyncStream/BidirectionalAsyncStream.swift @@ -54,6 +54,9 @@ public class BidirectionalAsyncStream { if case let .finished(value) = finished { throw StopIteration(value: value) } + if case let .error(value) = finished { + throw value + } if started { throw WrongStreamUse( message: "The BidirectionalSyncStream has already started, " + @@ -72,6 +75,10 @@ public class BidirectionalAsyncStream { finished = .finished(value) throw StopIteration(value: value) + case let .error(value): + finished = .error(value) + throw value + default: throw WrongStreamUse(message: "yield or return must be called in the continuation closure") } @@ -99,6 +106,9 @@ public class BidirectionalAsyncStream { if case let .finished(value) = finished { throw StopIteration(value: value) } + if case let .error(value) = finished { + throw value + } continuation.sendValue = element continuation.state = .sended(element) @@ -113,6 +123,10 @@ public class BidirectionalAsyncStream { finished = .finished(value) throw StopIteration(value: value) + case let .error(value): + finished = .error(value) + throw value + default: throw WrongStreamUse(message: "yield or return must be called in the continuation closure") } @@ -126,6 +140,7 @@ public class BidirectionalAsyncStream { case waitingForSend case sended(SendT) case finished(ReturnT) + case error(Terminated) } // MARK: Private @@ -185,6 +200,36 @@ public extension BidirectionalAsyncStream { await yieldSemaphore.signal() } + /// Throws an error to the stream and finishes the stream. + /// This is the last call in the stream. + /// + /// - Parameters: + /// - error: The error to throw. + public func `throw`( + error: any Error, + fileName: String = #file, + functionName: String = #function, + lineNumber: Int = #line, + columnNumber: Int = #column + ) async { + if finished { + fatalError("The stream has finished. Cannot return any more.") + } + + finished = true + + let filename = (fileName as NSString).lastPathComponent + let terminated = Terminated( + fileName: fileName, + functionName: functionName, + lineNumber: lineNumber, + columnNumber: columnNumber, + error: error + ) + state = .error(terminated) + await yieldSemaphore.signal() + } + // MARK: Internal internal var state: State = .idle diff --git a/Sources/SyncStream/BidirectionalSyncStream.swift b/Sources/SyncStream/BidirectionalSyncStream.swift index 98a3755..6d49be1 100644 --- a/Sources/SyncStream/BidirectionalSyncStream.swift +++ b/Sources/SyncStream/BidirectionalSyncStream.swift @@ -29,10 +29,35 @@ public struct WrongStreamUse: Error { public var message: String } +// MARK: - Terminated + +/// An error to indicate that the stream has been terminated. +/// i.e. an error has occurred in the stream. +public struct Terminated: Error { + /// The file name where the error occurred. + public var fileName: String + /// The function name where the error occurred. + public var functionName: String + /// The line number where the error occurred. + public var lineNumber: Int + /// The column number where the error occurred. + public var columnNumber: Int + /// The error that occurred. + public var error: any Error + + public var localizedDescription: String { + "Terminated in \(fileName) at \(functionName):\(lineNumber):\(columnNumber) " + + "with error: \(error.localizedDescription)" + } +} + // MARK: - NoneType /// A type to represent `None` in Python. -public struct NoneType {} +public struct NoneType { + /// Creates a new `NoneType`. + public init() {} +} // MARK: - BidirectionalSyncStream @@ -72,6 +97,9 @@ public class BidirectionalSyncStream { if case let .finished(value) = finished { throw StopIteration(value: value) } + if case let .error(value) = finished { + throw value + } if started { throw WrongStreamUse( message: "The BidirectionalSyncStream has already started, " + @@ -90,6 +118,10 @@ public class BidirectionalSyncStream { finished = .finished(value) throw StopIteration(value: value) + case let .error(value): + finished = .error(value) + throw value + default: throw WrongStreamUse(message: "yield or return must be called in the continuation closure") } @@ -117,6 +149,9 @@ public class BidirectionalSyncStream { if case let .finished(value) = finished { throw StopIteration(value: value) } + if case let .error(value) = finished { + throw value + } continuation.sendValue = element continuation.state = .sended(element) @@ -131,6 +166,10 @@ public class BidirectionalSyncStream { finished = .finished(value) throw StopIteration(value: value) + case let .error(value): + finished = .error(value) + throw value + default: throw WrongStreamUse(message: "yield or return must be called in the continuation closure") } @@ -144,6 +183,7 @@ public class BidirectionalSyncStream { case waitingForSend case sended(SendT) case finished(ReturnT) + case error(Terminated) } // MARK: Private @@ -204,6 +244,36 @@ public extension BidirectionalSyncStream { yieldSemaphore.signal() } + /// Throws an error to the stream and finishes the stream. + /// This is the last call in the stream. + /// + /// - Parameters: + /// - error: The error to throw. + public func `throw`( + error: any Error, + fileName: String = #file, + functionName: String = #function, + lineNumber: Int = #line, + columnNumber: Int = #column + ) { + if finished { + fatalError("The stream has finished. Cannot return any more.") + } + + finished = true + + let filename = (fileName as NSString).lastPathComponent + let terminated = Terminated( + fileName: fileName, + functionName: functionName, + lineNumber: lineNumber, + columnNumber: columnNumber, + error: error + ) + state = .error(terminated) + yieldSemaphore.signal() + } + // MARK: Internal internal var state: State = .idle