Skip to content

Add an overload that allows supplying custom stdout and stderr destinations AND a body #61

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

Merged
merged 1 commit into from
Jun 10, 2025
Merged
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
61 changes: 61 additions & 0 deletions Sources/Subprocess/API.swift
Original file line number Diff line number Diff line change
@@ -142,6 +142,67 @@ public func run<

// MARK: - Custom Execution Body

/// Run an executable with given parameters and a custom closure
/// to manage the running subprocess' lifetime and stream its standard output.
/// - Parameters:
/// - executable: The executable to run.
/// - arguments: The arguments to pass to the executable.
/// - environment: The environment in which to run the executable.
/// - workingDirectory: The working directory in which to run the executable.
/// - platformOptions: The platform specific options to use
/// when running the executable.
/// - input: The input to send to the executable.
/// - output: How to manager executable standard output.
/// - error: How to manager executable standard error.
/// - isolation: the isolation context to run the body closure.
/// - body: The custom execution body to manually control the running process
/// - Returns an executableResult type containing the return value
/// of the closure.
public func run<Result, Input: InputProtocol, Output: OutputProtocol, Error: OutputProtocol>(
_ executable: Executable,
arguments: Arguments = [],
environment: Environment = .inherit,
workingDirectory: FilePath? = nil,
platformOptions: PlatformOptions = PlatformOptions(),
input: Input = .none,
output: Output = .discarded,
error: Error = .discarded,
isolation: isolated (any Actor)? = #isolation,
body: ((Execution) async throws -> Result)
) async throws -> ExecutionResult<Result> where Error.OutputType == Void {
return try await Configuration(
executable: executable,
arguments: arguments,
environment: environment,
workingDirectory: workingDirectory,
platformOptions: platformOptions
).run(
input: try input.createPipe(),
output: try output.createPipe(),
error: try error.createPipe()
) { execution, inputIO, outputIO, errorIO in
var inputIOBox: TrackedPlatformDiskIO? = consume inputIO
return try await withThrowingTaskGroup(
of: Void.self,
returning: Result.self
) { group in
var inputIOContainer: TrackedPlatformDiskIO? = inputIOBox.take()
group.addTask {
if let inputIO = inputIOContainer.take() {
let writer = StandardInputWriter(diskIO: inputIO)
try await input.write(with: writer)
try await writer.finish()
}
}

// Body runs in the same isolation
let result = try await body(execution)
try await group.waitForAll()
return result
}
}
}

/// Run an executable with given parameters and a custom closure
/// to manage the running subprocess' lifetime and stream its standard output.
/// - Parameters: