-
Notifications
You must be signed in to change notification settings - Fork 31
Description
Describe the bug
I'm using collected results flow of swift-subprocess as a way to emulate a shell script. Typically, sequences of command executions are written linearly from top to bottom. Scripts tend to be written optimistically, assuming success for the most part until something fails, capturing output occasionally. While bash tends to skip over failed commands it is generally considered a good practice to use set -e
so that the script aborts on the first command that exits with an error, a bit like throwing an error. The collected result mode is the one that most closely approximates this since there's no need for nested functions, and input/output can be redirected and piped more easily.
With Swift, there is the standard Result enum. A script might look something like this attempting to preserve linear flow while adopting Swift Result.
// Assemble the final product
let someOutput = try doSomething().get() // <-- Result.get()
try doSomethingElse().get() // <-- Throws an error aborting the script if the result is a failure
let toolOutput = try runMyTool().get().standardOutput ?? ""
if !toolOutput.contains(someOutput) {
throw ScriptError("The tool output doesn't contain the requested path \(someOutput): \(toolOutput)")
}
In other styles of Swift programming Result also allows idiomatic switch statements too.
However, CollectedResult in swift-subprocess isn't a Swift Result, and doesn't behave like one either. Instead, the script has to do something like this to get the desired behaviour:
let result = try await run(..., output: .string(limit: 1024*1024))
guard result.terminationStatus.isSuccess else {
throw ScriptError("The tool exited with an error \(result.terminationStatus)")
}
let toolOutput = result.standardOutput ?? ""
This pattern must be repeated for potentially many different tool invocations and it doesn't fit into a typical Swift switch statement with exhaustive cases.
Expected behavior
I think that CollectedResult should be a Swift Result with both success and failure case. It is more ergonomic from both a linear scripting perspective, and typical idiomatic Swift code.