@@ -23,6 +23,97 @@ import Dispatch
23
23
24
24
import _Concurrency
25
25
26
+ public struct ProcessEnvironmentBlock {
27
+ #if os(Windows)
28
+ public typealias Key = CaseInsensitiveString
29
+ #else
30
+ public typealias Key = String
31
+ #endif
32
+ public typealias Value = String
33
+
34
+ private var storage : Dictionary < Key , Value >
35
+
36
+ private init ( storage: Dictionary < Key , Value > ) {
37
+ self . storage = storage
38
+ }
39
+
40
+ public init ( dictionary: Dictionary < String , String > = [ : ] ) {
41
+ #if os(Windows)
42
+ self . storage = . init( uniqueKeysWithValues: dictionary. map {
43
+ ( CaseInsensitiveString ( $0) , $1)
44
+ } )
45
+ #else
46
+ self . storage = dictionary
47
+ #endif
48
+ }
49
+
50
+ internal init < S: Sequence > ( uniqueKeysWithValues keysAndValues: S )
51
+ where S. Element == ( Key , Value ) {
52
+ storage = . init( uniqueKeysWithValues: keysAndValues)
53
+ }
54
+
55
+ public var dictionary : Dictionary < String , String > {
56
+ #if os(Windows)
57
+ return Dictionary < String , String > ( uniqueKeysWithValues: storage. map {
58
+ ( $0. value, $1)
59
+ } )
60
+ #else
61
+ return storage
62
+ #endif
63
+ }
64
+
65
+ public var isEmpty : Bool {
66
+ storage. isEmpty
67
+ }
68
+
69
+ public subscript( _ key: String ) -> Value ? {
70
+ get {
71
+ return storage [ Key ( key) ]
72
+ }
73
+ set {
74
+ storage [ Key ( key) ] = newValue
75
+ }
76
+ }
77
+
78
+ public subscript( _ key: String , default value: @autoclosure ( ) -> Value ) -> Value {
79
+ return storage [ Key ( key) , default: value ( ) ]
80
+ }
81
+
82
+ public func contains( _ key: String ) -> Bool {
83
+ return storage. keys. contains ( Key ( key) )
84
+ }
85
+
86
+ public mutating func merge< S: Sequence > ( _ other: S ,
87
+ uniquingKeysWith combine: ( Value , Value ) throws -> Value )
88
+ rethrows where S. Element == ( Key , Value ) {
89
+ try storage. merge ( other, uniquingKeysWith: combine)
90
+ }
91
+
92
+ public __consuming func merging< S: Sequence > ( _ other: __owned S,
93
+ uniquingKeysWith combine:
94
+ ( Value , Value ) throws -> Value )
95
+ rethrows -> ProcessEnvironmentBlock where S. Element == ( Key , Value ) {
96
+ return try ProcessEnvironmentBlock ( storage: storage. merging ( other, uniquingKeysWith: combine) )
97
+ }
98
+ }
99
+
100
+ extension ProcessEnvironmentBlock : Codable {
101
+ }
102
+
103
+ extension ProcessEnvironmentBlock : Equatable {
104
+ }
105
+
106
+ extension ProcessEnvironmentBlock : Hashable {
107
+ }
108
+
109
+ extension ProcessEnvironmentBlock : Sequence {
110
+ public typealias Iterator = Dictionary < Key , Value > . Iterator
111
+
112
+ public __consuming func makeIterator( ) -> Self . Iterator {
113
+ storage. makeIterator ( )
114
+ }
115
+ }
116
+
26
117
/// Process result data which is available after process termination.
27
118
public struct ProcessResult : CustomStringConvertible , Sendable {
28
119
@@ -53,7 +144,7 @@ public struct ProcessResult: CustomStringConvertible, Sendable {
53
144
public let arguments : [ String ]
54
145
55
146
/// The environment with which the process was launched.
56
- public let environment : [ String : String ]
147
+ public let environment : ProcessEnvironmentBlock
57
148
58
149
/// The exit status of the process.
59
150
public let exitStatus : ExitStatus
@@ -71,7 +162,7 @@ public struct ProcessResult: CustomStringConvertible, Sendable {
71
162
/// See `waitpid(2)` for information on the exit status code.
72
163
public init (
73
164
arguments: [ String ] ,
74
- environment: [ String : String ] ,
165
+ environment: ProcessEnvironmentBlock ,
75
166
exitStatusCode: Int32 ,
76
167
normal: Bool ,
77
168
output: Result < [ UInt8 ] , Swift . Error > ,
@@ -99,7 +190,7 @@ public struct ProcessResult: CustomStringConvertible, Sendable {
99
190
/// Create an instance using an exit status and output result.
100
191
public init (
101
192
arguments: [ String ] ,
102
- environment: [ String : String ] ,
193
+ environment: ProcessEnvironmentBlock ,
103
194
exitStatus: ExitStatus ,
104
195
output: Result < [ UInt8 ] , Swift . Error > ,
105
196
stderrOutput: Result < [ UInt8 ] , Swift . Error >
@@ -285,7 +376,7 @@ public final class Process {
285
376
public let arguments : [ String ]
286
377
287
378
/// The environment with which the process was executed.
288
- public let environment : [ String : String ]
379
+ public let environment : ProcessEnvironmentBlock
289
380
290
381
/// The path to the directory under which to run the process.
291
382
public let workingDirectory : AbsolutePath ?
@@ -359,7 +450,7 @@ public final class Process {
359
450
@available ( macOS 10 . 15 , * )
360
451
public init (
361
452
arguments: [ String ] ,
362
- environment: [ String : String ] = ProcessEnv . vars,
453
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
363
454
workingDirectory: AbsolutePath ,
364
455
outputRedirection: OutputRedirection = . collect,
365
456
startNewProcessGroup: Bool = true ,
@@ -379,7 +470,7 @@ public final class Process {
379
470
@available ( macOS 10 . 15 , * )
380
471
public convenience init (
381
472
arguments: [ String ] ,
382
- environment: [ String : String ] = ProcessEnv . vars,
473
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
383
474
workingDirectory: AbsolutePath ,
384
475
outputRedirection: OutputRedirection = . collect,
385
476
verbose: Bool ,
@@ -411,7 +502,7 @@ public final class Process {
411
502
/// - loggingHandler: Handler for logging messages
412
503
public init (
413
504
arguments: [ String ] ,
414
- environment: [ String : String ] = ProcessEnv . vars,
505
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
415
506
outputRedirection: OutputRedirection = . collect,
416
507
startNewProcessGroup: Bool = true ,
417
508
loggingHandler: LoggingHandler ? = . none
@@ -428,7 +519,7 @@ public final class Process {
428
519
@available ( * , deprecated, message: " use version without verbosity flag " )
429
520
public convenience init (
430
521
arguments: [ String ] ,
431
- environment: [ String : String ] = ProcessEnv . vars,
522
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
432
523
outputRedirection: OutputRedirection = . collect,
433
524
verbose: Bool = Process . verbose,
434
525
startNewProcessGroup: Bool = true
@@ -444,7 +535,7 @@ public final class Process {
444
535
445
536
public convenience init (
446
537
args: String ... ,
447
- environment: [ String : String ] = ProcessEnv . vars,
538
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
448
539
outputRedirection: OutputRedirection = . collect,
449
540
loggingHandler: LoggingHandler ? = . none
450
541
) {
@@ -536,7 +627,7 @@ public final class Process {
536
627
process. currentDirectoryURL = workingDirectory. asURL
537
628
}
538
629
process. executableURL = executablePath. asURL
539
- process. environment = environment
630
+ process. environment = environment. dictionary
540
631
541
632
let stdinPipe = Pipe ( )
542
633
process. standardInput = stdinPipe
@@ -989,7 +1080,7 @@ extension Process {
989
1080
@available ( macOS 10 . 15 , iOS 13 . 0 , tvOS 13 . 0 , watchOS 6 . 0 , * )
990
1081
static public func popen(
991
1082
arguments: [ String ] ,
992
- environment: [ String : String ] = ProcessEnv . vars,
1083
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
993
1084
loggingHandler: LoggingHandler ? = . none
994
1085
) async throws -> ProcessResult {
995
1086
let process = Process (
@@ -1012,7 +1103,7 @@ extension Process {
1012
1103
@available ( macOS 10 . 15 , iOS 13 . 0 , tvOS 13 . 0 , watchOS 6 . 0 , * )
1013
1104
static public func popen(
1014
1105
args: String ... ,
1015
- environment: [ String : String ] = ProcessEnv . vars,
1106
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
1016
1107
loggingHandler: LoggingHandler ? = . none
1017
1108
) async throws -> ProcessResult {
1018
1109
try await popen ( arguments: args, environment: environment, loggingHandler: loggingHandler)
@@ -1030,7 +1121,7 @@ extension Process {
1030
1121
@discardableResult
1031
1122
static public func checkNonZeroExit(
1032
1123
arguments: [ String ] ,
1033
- environment: [ String : String ] = ProcessEnv . vars,
1124
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
1034
1125
loggingHandler: LoggingHandler ? = . none
1035
1126
) async throws -> String {
1036
1127
let result = try await popen ( arguments: arguments, environment: environment, loggingHandler: loggingHandler)
@@ -1053,7 +1144,7 @@ extension Process {
1053
1144
@discardableResult
1054
1145
static public func checkNonZeroExit(
1055
1146
args: String ... ,
1056
- environment: [ String : String ] = ProcessEnv . vars,
1147
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
1057
1148
loggingHandler: LoggingHandler ? = . none
1058
1149
) async throws -> String {
1059
1150
try await checkNonZeroExit ( arguments: args, environment: environment, loggingHandler: loggingHandler)
@@ -1075,7 +1166,7 @@ extension Process {
1075
1166
// #endif
1076
1167
static public func popen(
1077
1168
arguments: [ String ] ,
1078
- environment: [ String : String ] = ProcessEnv . vars,
1169
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
1079
1170
loggingHandler: LoggingHandler ? = . none,
1080
1171
queue: DispatchQueue ? = nil ,
1081
1172
completion: @escaping ( Result < ProcessResult , Swift . Error > ) -> Void
@@ -1113,7 +1204,7 @@ extension Process {
1113
1204
@discardableResult
1114
1205
static public func popen(
1115
1206
arguments: [ String ] ,
1116
- environment: [ String : String ] = ProcessEnv . vars,
1207
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
1117
1208
loggingHandler: LoggingHandler ? = . none
1118
1209
) throws -> ProcessResult {
1119
1210
let process = Process (
@@ -1140,7 +1231,7 @@ extension Process {
1140
1231
@discardableResult
1141
1232
static public func popen(
1142
1233
args: String ... ,
1143
- environment: [ String : String ] = ProcessEnv . vars,
1234
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
1144
1235
loggingHandler: LoggingHandler ? = . none
1145
1236
) throws -> ProcessResult {
1146
1237
return try Process . popen ( arguments: args, environment: environment, loggingHandler: loggingHandler)
@@ -1160,7 +1251,7 @@ extension Process {
1160
1251
@discardableResult
1161
1252
static public func checkNonZeroExit(
1162
1253
arguments: [ String ] ,
1163
- environment: [ String : String ] = ProcessEnv . vars,
1254
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
1164
1255
loggingHandler: LoggingHandler ? = . none
1165
1256
) throws -> String {
1166
1257
let process = Process (
@@ -1192,7 +1283,7 @@ extension Process {
1192
1283
@discardableResult
1193
1284
static public func checkNonZeroExit(
1194
1285
args: String ... ,
1195
- environment: [ String : String ] = ProcessEnv . vars,
1286
+ environment: ProcessEnvironmentBlock = ProcessEnv . vars,
1196
1287
loggingHandler: LoggingHandler ? = . none
1197
1288
) throws -> String {
1198
1289
return try checkNonZeroExit ( arguments: args, environment: environment, loggingHandler: loggingHandler)
0 commit comments