@@ -352,6 +352,45 @@ extension __CommandLineArguments_v0: Codable {
352352 }
353353}
354354
355+ extension RandomAccessCollection < String > {
356+ /// Get the value of the command line argument with the given name.
357+ ///
358+ /// - Parameters:
359+ /// - label: The label or name of the argument, e.g. `"--attachments-path"`.
360+ /// - index: The index where `label` should be found, or `nil` to search the
361+ /// entire collection.
362+ ///
363+ /// - Returns: The value of the argument named by `label` at `index`. If no
364+ /// value is available, or if `index` is not `nil` and the argument at
365+ /// `index` is not named `label`, returns `nil`.
366+ ///
367+ /// This function handles arguments of the form `--label value` and
368+ /// `--label=value`. Other argument syntaxes are not supported.
369+ fileprivate func argumentValue( forLabel label: String , at index: Index ? = nil ) -> String ? {
370+ guard let index else {
371+ return indices. lazy
372+ . compactMap { argumentValue ( forLabel: label, at: $0) }
373+ . first
374+ }
375+
376+ let element = self [ index]
377+ if element == label {
378+ let nextIndex = self . index ( after: index)
379+ if nextIndex < endIndex {
380+ return self [ nextIndex]
381+ }
382+ } else {
383+ // Find an element equal to something like "--foo=bar" and split it.
384+ let prefix = " \( label) = "
385+ if element. hasPrefix ( prefix) , let equalsIndex = element. firstIndex ( of: " = " ) {
386+ return String ( element [ equalsIndex... ] . dropFirst ( ) )
387+ }
388+ }
389+
390+ return nil
391+ }
392+ }
393+
355394/// Initialize this instance given a sequence of command-line arguments passed
356395/// from Swift Package Manager.
357396///
@@ -366,10 +405,6 @@ func parseCommandLineArguments(from args: [String]) throws -> __CommandLineArgum
366405 // Do not consider the executable path AKA argv[0].
367406 let args = args. dropFirst ( )
368407
369- func isLastArgument( at index: [ String ] . Index) -> Bool {
370- args. index ( after: index) >= args. endIndex
371- }
372-
373408#if !SWT_NO_FILE_IO
374409#if canImport(Foundation)
375410 // Configuration for the test run passed in as a JSON file (experimental)
@@ -379,9 +414,7 @@ func parseCommandLineArguments(from args: [String]) throws -> __CommandLineArgum
379414 // NOTE: While the output event stream is opened later, it is necessary to
380415 // open the configuration file early (here) in order to correctly construct
381416 // the resulting __CommandLineArguments_v0 instance.
382- if let configurationIndex = args. firstIndex ( of: " --configuration-path " ) ?? args. firstIndex ( of: " --experimental-configuration-path " ) ,
383- !isLastArgument( at: configurationIndex) {
384- let path = args [ args. index ( after: configurationIndex) ]
417+ if let path = args. argumentValue ( forLabel: " --configuration-path " ) ?? args. argumentValue ( forLabel: " --experimental-configuration-path " ) {
385418 let file = try FileHandle ( forReadingAtPath: path)
386419 let configurationJSON = try file. readToEnd ( )
387420 result = try configurationJSON. withUnsafeBufferPointer { configurationJSON in
@@ -394,24 +427,22 @@ func parseCommandLineArguments(from args: [String]) throws -> __CommandLineArgum
394427 }
395428
396429 // Event stream output
397- if let eventOutputIndex = args. firstIndex ( of: " --event-stream-output-path " ) ?? args. firstIndex ( of: " --experimental-event-stream-output " ) ,
398- !isLastArgument( at: eventOutputIndex) {
399- result. eventStreamOutputPath = args [ args. index ( after: eventOutputIndex) ]
430+ if let path = args. argumentValue ( forLabel: " --event-stream-output-path " ) ?? args. argumentValue ( forLabel: " --experimental-event-stream-output " ) {
431+ result. eventStreamOutputPath = path
400432 }
433+
401434 // Event stream version
402435 do {
403- var eventOutputVersionIndex : Array < String > . Index ?
436+ var versionString : String ?
404437 var allowExperimental = false
405- eventOutputVersionIndex = args. firstIndex ( of : " --event-stream-version " )
406- if eventOutputVersionIndex == nil {
407- eventOutputVersionIndex = args. firstIndex ( of : " --experimental-event-stream-version " )
408- if eventOutputVersionIndex != nil {
438+ versionString = args. argumentValue ( forLabel : " --event-stream-version " )
439+ if versionString == nil {
440+ versionString = args. argumentValue ( forLabel : " --experimental-event-stream-version " )
441+ if versionString != nil {
409442 allowExperimental = true
410443 }
411444 }
412- if let eventOutputVersionIndex, !isLastArgument( at: eventOutputVersionIndex) {
413- let versionString = args [ args. index ( after: eventOutputVersionIndex) ]
414-
445+ if let versionString {
415446 // If the caller specified a version that could not be parsed, treat it as
416447 // an invalid argument.
417448 guard let eventStreamVersion = VersionNumber ( versionString) else {
@@ -432,14 +463,13 @@ func parseCommandLineArguments(from args: [String]) throws -> __CommandLineArgum
432463#endif
433464
434465 // XML output
435- if let xunitOutputIndex = args. firstIndex ( of : " --xunit-output " ) , !isLastArgument ( at : xunitOutputIndex ) {
436- result. xunitOutput = args [ args . index ( after : xunitOutputIndex ) ]
466+ if let xunitOutputPath = args. argumentValue ( forLabel : " --xunit-output " ) {
467+ result. xunitOutput = xunitOutputPath
437468 }
438469
439470 // Attachment output
440- if let attachmentsPathIndex = args. firstIndex ( of: " --attachments-path " ) ?? args. firstIndex ( of: " --experimental-attachments-path " ) ,
441- !isLastArgument( at: attachmentsPathIndex) {
442- result. attachmentsPath = args [ args. index ( after: attachmentsPathIndex) ]
471+ if let attachmentsPath = args. argumentValue ( forLabel: " --attachments-path " ) ?? args. argumentValue ( forLabel: " --experimental-attachments-path " ) {
472+ result. attachmentsPath = attachmentsPath
443473 }
444474#endif
445475
@@ -457,13 +487,12 @@ func parseCommandLineArguments(from args: [String]) throws -> __CommandLineArgum
457487 }
458488
459489 // Whether or not to symbolicate backtraces in the event stream.
460- if let symbolicateBacktracesIndex = args. firstIndex ( of : " --symbolicate-backtraces " ) , !isLastArgument ( at : symbolicateBacktracesIndex ) {
461- result. symbolicateBacktraces = args [ args . index ( after : symbolicateBacktracesIndex ) ]
490+ if let symbolicateBacktraces = args. argumentValue ( forLabel : " --symbolicate-backtraces " ) {
491+ result. symbolicateBacktraces = symbolicateBacktraces
462492 }
463493
464494 // Verbosity
465- if let verbosityIndex = args. firstIndex ( of: " --verbosity " ) , !isLastArgument( at: verbosityIndex) ,
466- let verbosity = Int ( args [ args. index ( after: verbosityIndex) ] ) {
495+ if let verbosity = args. argumentValue ( forLabel: " --verbosity " ) . flatMap ( Int . init) {
467496 result. verbosity = verbosity
468497 }
469498 if args. contains ( " --verbose " ) || args. contains ( " -v " ) {
@@ -478,9 +507,7 @@ func parseCommandLineArguments(from args: [String]) throws -> __CommandLineArgum
478507
479508 // Filtering
480509 func filterValues( forArgumentsWithLabel label: String ) -> [ String ] {
481- args. indices. lazy
482- . filter { args [ $0] == label && $0 < args. endIndex }
483- . map { args [ args. index ( after: $0) ] }
510+ args. indices. compactMap { args. argumentValue ( forLabel: label, at: $0) }
484511 }
485512 let filter = filterValues ( forArgumentsWithLabel: " --filter " )
486513 if !filter. isEmpty {
@@ -492,11 +519,11 @@ func parseCommandLineArguments(from args: [String]) throws -> __CommandLineArgum
492519 }
493520
494521 // Set up the iteration policy for the test run.
495- if let repetitionsIndex = args. firstIndex ( of : " --repetitions " ) , !isLastArgument ( at : repetitionsIndex ) {
496- result. repetitions = Int ( args [ args . index ( after : repetitionsIndex ) ] )
522+ if let repetitions = args. argumentValue ( forLabel : " --repetitions " ) . flatMap ( Int . init ) {
523+ result. repetitions = repetitions
497524 }
498- if let repeatUntilIndex = args. firstIndex ( of : " --repeat-until " ) , !isLastArgument ( at : repeatUntilIndex ) {
499- result. repeatUntil = args [ args . index ( after : repeatUntilIndex ) ]
525+ if let repeatUntil = args. argumentValue ( forLabel : " --repeat-until " ) {
526+ result. repeatUntil = repeatUntil
500527 }
501528
502529 return result
0 commit comments