apple / swift-argument-parser

Straightforward, type-safe argument parsing for Swift
Apache License 2.0
3.31k stars 311 forks source link

An array with no default value should be interpreted as an argument with at least one required element #569

Closed dabrahams closed 1 year ago

dabrahams commented 1 year ago
struct Example: ParsableCommand {
    @Argument var files: [String] = []
    @Option var count: Int?
    @Option var index = 0
    @Flag var verbose = false
    @Flag var stripWhitespace = false
}

In this example from the docs, if I wanted to require that at least one file was supplied, I think I should be able to write:

struct Example: ParsableCommand {
    @Argument var files: [String]
    @Option var count: Int?
    @Option var index = 0
    @Flag var verbose = false
    @Flag var stripWhitespace = false
}
rauhul commented 1 year ago

(Disclaimer: I'm not in the office this week nor have access to a laptop)

I believe your example should work as you described and if it doesn't this is a bug.

Could you attach the swift/argument-parser versions as well as the actual vs expected CLI output?

I will try to take a look asap when I return from break 😃

dabrahams commented 1 year ago

Not in a position to check right now, but it may be that this just fails when using a transform on the elements

rauhul commented 1 year ago

FWIW the two snippets above prepended with the following snippet work as expected:

import ArgumentParser

@main
extension Example { 
    func run() throws { print(self) }
}

Swift: 5.9 ArgumentParser: 1.2.2 (fee6933f37fde9a5e12a1e4aeaa93fe60116ff2a)

natecook1000 commented 1 year ago

Hi @dabrahams! I've confirmed that this works as expected, with both transformed and ExpressibleByArgument element types. If you're still seeing this, feel free to re-open with a reproducing example.

Here's an example with a transform closure:

@main
struct Repeat: ParsableCommand {
  @Option(help: "The number of times to repeat 'phrases'. (default: forever)")
  var count: Int?

  @Argument(help: "The phrases to repeat.", transform: { $0.uppercased() })
  var phrases: [String]

  mutating func run() throws {
    for _ in 1...(count ?? .max) {
      print(phrases.joined(separator: "\n"))
    }
  }
}

Output:

$ swift run repeat
Error: Missing expected argument '<phrases> ...'

USAGE: repeat [--count <count>] <phrases> ...

ARGUMENTS:
  <phrases>               The phrases to repeat.

OPTIONS:
  --count <count>         The number of times to repeat 'phrases'. (default: forever)
  -h, --help              Show help information.

$ swift run repeat one two three --count 2
ONE
TWO
THREE
ONE
TWO
THREE
dabrahams commented 1 year ago

Thanks for checking it out. I'm sorry if this report wasted your time; I filed it from a memory of something that hadn't worked out, without a reproducer.