Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(BidirectionalStream): build support throws #4

Merged
merged 2 commits into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .swiftlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
45 changes: 45 additions & 0 deletions Sources/SyncStream/BidirectionalAsyncStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public class BidirectionalAsyncStream<YieldT, SendT, ReturnT> {
if case let .finished(value) = finished {
throw StopIteration<ReturnT>(value: value)
}
if case let .error(value) = finished {
throw value
}
if started {
throw WrongStreamUse(
message: "The BidirectionalSyncStream has already started, " +
Expand All @@ -72,6 +75,10 @@ public class BidirectionalAsyncStream<YieldT, SendT, ReturnT> {
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")
}
Expand Down Expand Up @@ -99,6 +106,9 @@ public class BidirectionalAsyncStream<YieldT, SendT, ReturnT> {
if case let .finished(value) = finished {
throw StopIteration<ReturnT>(value: value)
}
if case let .error(value) = finished {
throw value
}

continuation.sendValue = element
continuation.state = .sended(element)
Expand All @@ -113,6 +123,10 @@ public class BidirectionalAsyncStream<YieldT, SendT, ReturnT> {
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")
}
Expand All @@ -126,6 +140,7 @@ public class BidirectionalAsyncStream<YieldT, SendT, ReturnT> {
case waitingForSend
case sended(SendT)
case finished(ReturnT)
case error(Terminated)
}

// MARK: Private
Expand Down Expand Up @@ -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
Expand Down
72 changes: 71 additions & 1 deletion Sources/SyncStream/BidirectionalSyncStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -72,6 +97,9 @@ public class BidirectionalSyncStream<YieldT, SendT, ReturnT> {
if case let .finished(value) = finished {
throw StopIteration<ReturnT>(value: value)
}
if case let .error(value) = finished {
throw value
}
if started {
throw WrongStreamUse(
message: "The BidirectionalSyncStream has already started, " +
Expand All @@ -90,6 +118,10 @@ public class BidirectionalSyncStream<YieldT, SendT, ReturnT> {
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")
}
Expand Down Expand Up @@ -117,6 +149,9 @@ public class BidirectionalSyncStream<YieldT, SendT, ReturnT> {
if case let .finished(value) = finished {
throw StopIteration<ReturnT>(value: value)
}
if case let .error(value) = finished {
throw value
}

continuation.sendValue = element
continuation.state = .sended(element)
Expand All @@ -131,6 +166,10 @@ public class BidirectionalSyncStream<YieldT, SendT, ReturnT> {
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")
}
Expand All @@ -144,6 +183,7 @@ public class BidirectionalSyncStream<YieldT, SendT, ReturnT> {
case waitingForSend
case sended(SendT)
case finished(ReturnT)
case error(Terminated)
}

// MARK: Private
Expand Down Expand Up @@ -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
Expand Down