Skip to content

Commit 25754a8

Browse files
committed
Adopt new ErrorOutputProtocol
1 parent ae4e64f commit 25754a8

File tree

2 files changed

+17
-211
lines changed

2 files changed

+17
-211
lines changed

Sources/Subprocess/PipeConfiguration.swift

Lines changed: 15 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ infix operator |> : AdditionPrecedence
4444
public enum ErrorRedirection: Sendable {
4545
/// Keep stderr separate (default behavior)
4646
case separate
47-
/// Redirect stderr to stdout, replacing stdout entirely (stdout -> /dev/null)
48-
case replaceStdout
4947
/// Merge stderr into stdout (both go to the same destination)
5048
case mergeWithStdout
5149
}
@@ -63,9 +61,6 @@ public struct ProcessStageOptions: Sendable {
6361
/// Default options (no redirection)
6462
public static let `default` = ProcessStageOptions()
6563

66-
/// Redirect stderr to stdout, discarding original stdout
67-
public static let stderrToStdout = ProcessStageOptions(errorRedirection: .replaceStdout)
68-
6964
/// Merge stderr with stdout
7065
public static let mergeErrors = ProcessStageOptions(errorRedirection: .mergeWithStdout)
7166
}
@@ -143,7 +138,7 @@ public struct PipeStage: Sendable {
143138
public struct PipeConfiguration<
144139
Input: InputProtocol,
145140
Output: OutputProtocol,
146-
Error: OutputProtocol
141+
Error: ErrorOutputProtocol
147142
>: Sendable, CustomStringConvertible {
148143
/// Array of process stages in the pipeline
149144
internal var stages: [PipeStage]
@@ -215,7 +210,7 @@ internal struct SendableCollectedResult: @unchecked Sendable {
215210
let standardOutput: Any
216211
let standardError: Any
217212

218-
init<Output: OutputProtocol, Error: OutputProtocol>(_ result: CollectedResult<Output, Error>) {
213+
init<Output: OutputProtocol, Error: ErrorOutputProtocol>(_ result: CollectedResult<Output, Error>) {
219214
self.processIdentifier = result.processIdentifier
220215
self.terminationStatus = result.terminationStatus
221216
self.standardOutput = result.standardOutput
@@ -290,42 +285,13 @@ extension PipeConfiguration {
290285
output: self.output,
291286
error: self.error
292287
)
293-
294-
case .replaceStdout:
295-
// Redirect stderr to stdout, discard original stdout
296-
let result = try await Subprocess.run(
297-
configuration,
298-
input: self.input,
299-
output: .discarded,
300-
error: self.output
301-
)
302-
303-
let emptyError: Error.OutputType =
304-
if Error.OutputType.self == Void.self {
305-
() as! Error.OutputType
306-
} else if Error.OutputType.self == String?.self {
307-
String?.none as! Error.OutputType
308-
} else if Error.OutputType.self == [UInt8]?.self {
309-
[UInt8]?.none as! Error.OutputType
310-
} else {
311-
fatalError()
312-
}
313-
314-
// Create a new result with the error output as the standard output
315-
return CollectedResult(
316-
processIdentifier: result.processIdentifier,
317-
terminationStatus: result.terminationStatus,
318-
standardOutput: result.standardError,
319-
standardError: emptyError
320-
)
321-
322288
case .mergeWithStdout:
323289
// Redirect stderr to stdout, merge both streams
324290
let finalResult = try await Subprocess.run(
325291
configuration,
326292
input: self.input,
327293
output: self.output,
328-
error: self.output
294+
error: .combineWithOutput
329295
)
330296

331297
let emptyError: Error.OutputType =
@@ -440,23 +406,6 @@ extension PipeConfiguration {
440406
error: FileDescriptorOutput(fileDescriptor: sharedErrorPipe.writeEnd, closeAfterSpawningProcess: false)
441407
)
442408

443-
taskResult = PipelineTaskResult.success(
444-
0,
445-
SendableCollectedResult(
446-
CollectedResult<FileDescriptorOutput, DiscardedOutput>(
447-
processIdentifier: originalResult.processIdentifier,
448-
terminationStatus: originalResult.terminationStatus,
449-
standardOutput: (),
450-
standardError: ()
451-
)))
452-
case .replaceStdout:
453-
let originalResult = try await Subprocess.run(
454-
configuration,
455-
input: self.input,
456-
output: .discarded,
457-
error: .fileDescriptor(writeEnd, closeAfterSpawningProcess: true)
458-
)
459-
460409
taskResult = PipelineTaskResult.success(
461410
0,
462411
SendableCollectedResult(
@@ -471,7 +420,7 @@ extension PipeConfiguration {
471420
configuration,
472421
input: self.input,
473422
output: .fileDescriptor(writeEnd, closeAfterSpawningProcess: false),
474-
error: .fileDescriptor(writeEnd, closeAfterSpawningProcess: false)
423+
error: .combineWithOutput
475424
)
476425

477426
try writeEnd.close()
@@ -586,23 +535,6 @@ extension PipeConfiguration {
586535
error: FileDescriptorOutput(fileDescriptor: sharedErrorPipe.writeEnd, closeAfterSpawningProcess: false)
587536
)
588537

589-
taskResult = PipelineTaskResult.success(
590-
i,
591-
SendableCollectedResult(
592-
CollectedResult<FileDescriptorOutput, DiscardedOutput>(
593-
processIdentifier: originalResult.processIdentifier,
594-
terminationStatus: originalResult.terminationStatus,
595-
standardOutput: (),
596-
standardError: ()
597-
)))
598-
case .replaceStdout:
599-
let originalResult = try await Subprocess.run(
600-
configuration,
601-
input: .fileDescriptor(readEnd, closeAfterSpawningProcess: true),
602-
output: .discarded,
603-
error: .fileDescriptor(writeEnd, closeAfterSpawningProcess: true)
604-
)
605-
606538
taskResult = PipelineTaskResult.success(
607539
i,
608540
SendableCollectedResult(
@@ -617,7 +549,7 @@ extension PipeConfiguration {
617549
configuration,
618550
input: .fileDescriptor(readEnd, closeAfterSpawningProcess: true),
619551
output: .fileDescriptor(writeEnd, closeAfterSpawningProcess: false),
620-
error: .fileDescriptor(writeEnd, closeAfterSpawningProcess: false)
552+
error: .combineWithOutput
621553
)
622554

623555
try writeEnd.close()
@@ -711,101 +643,16 @@ extension PipeConfiguration {
711643
error: FileDescriptorOutput(fileDescriptor: sharedErrorPipe.writeEnd, closeAfterSpawningProcess: false)
712644
)
713645
return PipelineTaskResult.success(lastIndex, SendableCollectedResult(finalResult))
714-
case .replaceStdout:
715-
let finalResult = try await Subprocess.run(
716-
configuration,
717-
input: .fileDescriptor(readEnd, closeAfterSpawningProcess: true),
718-
output: .discarded,
719-
error: self.output
720-
)
721-
722-
let emptyError: Error.OutputType =
723-
if Error.OutputType.self == Void.self {
724-
() as! Error.OutputType
725-
} else if Error.OutputType.self == String?.self {
726-
String?.none as! Error.OutputType
727-
} else if Error.OutputType.self == [UInt8]?.self {
728-
[UInt8]?.none as! Error.OutputType
729-
} else {
730-
fatalError()
731-
}
732-
646+
case .mergeWithStdout:
733647
return PipelineTaskResult.success(
734648
lastIndex,
735649
SendableCollectedResult(
736-
CollectedResult<Output, Error>(
737-
processIdentifier: finalResult.processIdentifier,
738-
terminationStatus: finalResult.terminationStatus,
739-
standardOutput: finalResult.standardError,
740-
standardError: emptyError
650+
try await Subprocess.run(
651+
configuration,
652+
input: .fileDescriptor(readEnd, closeAfterSpawningProcess: true),
653+
output: self.output,
654+
error: .combineWithOutput
741655
)))
742-
case .mergeWithStdout:
743-
let finalResult = try await Subprocess.run(
744-
configuration,
745-
input: .fileDescriptor(readEnd, closeAfterSpawningProcess: true),
746-
output: self.output,
747-
error: self.output
748-
)
749-
750-
let emptyError: Error.OutputType =
751-
if Error.OutputType.self == Void.self {
752-
() as! Error.OutputType
753-
} else if Error.OutputType.self == String?.self {
754-
String?.none as! Error.OutputType
755-
} else if Error.OutputType.self == [UInt8]?.self {
756-
[UInt8]?.none as! Error.OutputType
757-
} else {
758-
fatalError()
759-
}
760-
761-
// Merge the different kinds of output types (string, fd, etc.)
762-
if Output.OutputType.self == Void.self {
763-
return PipelineTaskResult.success(
764-
lastIndex,
765-
SendableCollectedResult(
766-
CollectedResult<Output, Error>(
767-
processIdentifier: finalResult.processIdentifier,
768-
terminationStatus: finalResult.terminationStatus,
769-
standardOutput: () as! Output.OutputType,
770-
standardError: finalResult.standardOutput as! Error.OutputType
771-
)))
772-
} else if Output.OutputType.self == String?.self {
773-
let out: String? = finalResult.standardOutput as! String?
774-
let err: String? = finalResult.standardError as! String?
775-
776-
let finalOutput = (out ?? "") + (err ?? "")
777-
// FIXME reduce the final output to the output.maxSize number of bytes
778-
779-
return PipelineTaskResult.success(
780-
lastIndex,
781-
SendableCollectedResult(
782-
CollectedResult<Output, Error>(
783-
processIdentifier: finalResult.processIdentifier,
784-
terminationStatus: finalResult.terminationStatus,
785-
standardOutput: finalOutput as! Output.OutputType,
786-
standardError: emptyError
787-
)))
788-
} else if Output.OutputType.self == [UInt8].self {
789-
let out: [UInt8]? = finalResult.standardOutput as! [UInt8]?
790-
let err: [UInt8]? = finalResult.standardError as! [UInt8]?
791-
792-
var finalOutput = (out ?? []) + (err ?? [])
793-
if finalOutput.count > self.output.maxSize {
794-
finalOutput = [UInt8](finalOutput[...self.output.maxSize])
795-
}
796-
797-
return PipelineTaskResult.success(
798-
lastIndex,
799-
SendableCollectedResult(
800-
CollectedResult<Output, Error>(
801-
processIdentifier: finalResult.processIdentifier,
802-
terminationStatus: finalResult.terminationStatus,
803-
standardOutput: finalOutput as! Output.OutputType,
804-
standardError: emptyError
805-
)))
806-
} else {
807-
fatalError()
808-
}
809656
}
810657
case .swiftFunction(let function):
811658
let inputReadFileDescriptor = createIODescriptor(from: readEnd, closeWhenDone: true)
@@ -1086,7 +933,7 @@ extension Array where Element == PipeStage {
1086933
}
1087934

1088935
/// Create a PipeConfiguration from stages with specific input, output, and error types
1089-
public func finally<FinalInput: InputProtocol, FinalOutput: OutputProtocol, FinalError: OutputProtocol>(
936+
public func finally<FinalInput: InputProtocol, FinalOutput: OutputProtocol, FinalError: ErrorOutputProtocol>(
1090937
input: FinalInput,
1091938
output: FinalOutput,
1092939
error: FinalError
@@ -1100,7 +947,7 @@ extension Array where Element == PipeStage {
1100947
}
1101948

1102949
/// Create a PipeConfiguration from stages with no input and specific output and error types
1103-
public func finally<FinalOutput: OutputProtocol, FinalError: OutputProtocol>(
950+
public func finally<FinalOutput: OutputProtocol, FinalError: ErrorOutputProtocol>(
1104951
output: FinalOutput,
1105952
error: FinalError
1106953
) -> PipeConfiguration<NoInput, FinalOutput, FinalError> {
@@ -1116,15 +963,15 @@ extension Array where Element == PipeStage {
1116963
}
1117964

1118965
/// Final pipe operator for stage arrays with specific input, output and error types
1119-
public func |> <FinalInput: InputProtocol, FinalOutput: OutputProtocol, FinalError: OutputProtocol>(
966+
public func |> <FinalInput: InputProtocol, FinalOutput: OutputProtocol, FinalError: ErrorOutputProtocol>(
1120967
left: [PipeStage],
1121968
right: (input: FinalInput, output: FinalOutput, error: FinalError)
1122969
) -> PipeConfiguration<FinalInput, FinalOutput, FinalError> {
1123970
return left.finally(input: right.input, output: right.output, error: right.error)
1124971
}
1125972

1126973
/// Final pipe operator for stage arrays with specific output and error types
1127-
public func |> <FinalOutput: OutputProtocol, FinalError: OutputProtocol>(
974+
public func |> <FinalOutput: OutputProtocol, FinalError: ErrorOutputProtocol>(
1128975
left: [PipeStage],
1129976
right: (output: FinalOutput, error: FinalError)
1130977
) -> PipeConfiguration<NoInput, FinalOutput, FinalError> {

0 commit comments

Comments
 (0)