diff --git a/Sources/CommandLineToolSupport/Intramodular/AnyCommandLineTool.swift b/Sources/CommandLineToolSupport/Intramodular/AnyCommandLineTool.swift index 6a6c12a..fc1fcdf 100644 --- a/Sources/CommandLineToolSupport/Intramodular/AnyCommandLineTool.swift +++ b/Sources/CommandLineToolSupport/Intramodular/AnyCommandLineTool.swift @@ -30,3 +30,17 @@ open class AnyCommandLineTool { return result } } + +extension AnyCommandLineTool { + public func withUnsafeSystemShell( + sink: Process.StandardOutputSink, + perform operation: (SystemShell) async throws -> R + ) async throws -> R { + try await withUnsafeSystemShell { shell in + shell.options ??= [] + shell.options?.append(._forwardStdoutStderr(to: sink)) + + return try await operation(shell) + } + } +} diff --git a/Sources/Merge/Intramodular/Process/Process.StandardOutputSink.swift b/Sources/Merge/Intramodular/Process/Process.StandardOutputSink.swift index e7674a6..3066fa3 100644 --- a/Sources/Merge/Intramodular/Process/Process.StandardOutputSink.swift +++ b/Sources/Merge/Intramodular/Process/Process.StandardOutputSink.swift @@ -8,7 +8,7 @@ import System #if os(macOS) extension Process { - public enum StandardOutputSink { + public enum StandardOutputSink: Hashable { /// Redirect output to the terminal. case terminal /// Redirect output to the file at the given path, creating if necessary. diff --git a/Sources/Merge/Intramodular/Process/_AsyncProcess.swift b/Sources/Merge/Intramodular/Process/_AsyncProcess.swift index 377e7e9..8d8ac22 100644 --- a/Sources/Merge/Intramodular/Process/_AsyncProcess.swift +++ b/Sources/Merge/Intramodular/Process/_AsyncProcess.swift @@ -489,33 +489,39 @@ extension _AsyncProcess { } } - private func __handleData(_ data: Data, forPipe pipe: Pipe) async throws { + private func __handleData( + _ data: Data, + forPipe pipe: Pipe + ) async throws { guard !data.isEmpty else { return } let pipeName: Process.PipeName = try self.name(of: pipe) - - let forwardStdoutStderr: Bool = options.contains(._forwardStdoutStderr) - + switch pipeName { case .standardOutput: _publishers.standardOutputPublisher.send(data) - - if forwardStdoutStderr { - FileHandle.standardOutput.write(data) - } case .standardError: _publishers.standardErrorPublisher.send(data) - - if forwardStdoutStderr { - FileHandle.standardOutput.write(data) - } default: break } - guard let dataAsString = String(data: data, encoding: String.Encoding.utf8), !dataAsString.isEmpty else { + let forwardStdoutStderrToTerminal: Bool = options.contains(where: { $0._stdoutStderrSink == .terminal }) // FIXME: (@vmanot) unhandled cases + + if forwardStdoutStderrToTerminal { + switch pipeName { + case .standardOutput: + FileHandle.standardOutput.write(data) + case .standardError: + FileHandle.standardOutput.write(data) + default: + break + } + } + + guard let dataAsString: String = String(data: data, encoding: String.Encoding.utf8), !dataAsString.isEmpty else { return } @@ -717,7 +723,19 @@ extension _AsyncProcess { public enum Option: Hashable { case _useAppleScript case _useAuthorizationExecuteWithPrivileges - case _forwardStdoutStderr + case _forwardStdoutStderr(to: Process.StandardOutputSink) + + public static var _forwardStdoutStderr: Self { + ._forwardStdoutStderr(to: .terminal) + } + + public var _stdoutStderrSink: Process.StandardOutputSink { + guard case let ._forwardStdoutStderr(sink) = self else { + return .null + } + + return sink + } } } diff --git a/Sources/Merge/Intramodular/Shell Scripting/SystemShell.swift b/Sources/Merge/Intramodular/Shell Scripting/SystemShell.swift index 5b17004..ca07f13 100644 --- a/Sources/Merge/Intramodular/Shell Scripting/SystemShell.swift +++ b/Sources/Merge/Intramodular/Shell Scripting/SystemShell.swift @@ -11,7 +11,7 @@ public final class SystemShell { public var environmentVariables: [String: String] public var currentDirectoryURL: URL? - public let options: [_AsyncProcess.Option]? + public var options: [_AsyncProcess.Option]? public init( environment: [String: String]? = nil,