fsprojects / Argu

A declarative CLI argument parser for F#
https://fsprojects.github.io/Argu
MIT License
456 stars 74 forks source link

Mandatory arguments in nested subcommands #116

Closed b0wter closed 11 months ago

b0wter commented 6 years ago

I am trying to parse something like this:

./esp device status
./esp device status $myId
./esp device add $myId
./esp device remove $myId

To do so I am using this definition:

type DeviceIdArgs =
    | [<Mandatory>] [<MainCommand>] Id of string
    with
        interface IArgParserTemplate with
            member this.Usage =
                match this with
                | Id _ -> "Id of the device to remove"

[<RequireSubcommand>]
type DeviceArgs =
    | [<CliPrefix(CliPrefix.None)>] Status of ParseResults<DeviceIdArgs>
    | [<CliPrefix(CliPrefix.None)>] Remove of ParseResults<DeviceIdArgs>
    | [<CliPrefix(CliPrefix.None)>] Add of ParseResults<DeviceIdArgs>
    with
        interface IArgParserTemplate with
            member this.Usage =
                match this with
                | Status _ -> "lists all devices currently associated with your account"
                | Remove _ -> "removes the device with the given id from your account, use * to remove all"
                | Add _ -> "adds a new device with the given id, will return a registration token"

[<RequireSubcommand>]
type EspArgs =
    | Version
    | [<CliPrefix(CliPrefix.None)>] [<First>] Account of ParseResults<AccountArgs>
    | [<CliPrefix(CliPrefix.None)>] [<First>] Device of ParseResults<DeviceArgs>
    | [<CliPrefix(CliPrefix.None)>] [<First>] Firmware of ParseResults<FirmwareArgs>
    with
        interface IArgParserTemplate with
            member this.Usage =
                match this with
                | Version -> "echos the version of this tool"
                | Account _ -> "create a backend account, or login to one"
                | Device _ -> "list/remove/... devices from your account"
                | Firmware _ -> "upload and apply firmware"

This almost works like I want it to. I want to make sure that the user needs to supply an id with the commands. That why I used the MandatoryArgument for the id in the DeviceIdArgs. Currently ./esp device add is a valid command.

vrobinson commented 6 years ago

I am seeing this too:

type Direction =
    | Forward
    | Backward
    | Random

type SequenceArguments =
    | [<Mandatory>] Direction of Direction
    | Other of string
with
    interface IArgParserTemplate with
        member this.Usage =
            match this with
            | Direction _ -> "Specify the direction to use"
            | Other _ -> "Added only to test behavior when more than one arg supplied"

type MainArguments =
    | [<CliPrefix(CliPrefix.None)>] Sequence of ParseResults<SequenceArguments>
    | Version
with
    interface IArgParserTemplate with
        member this.Usage =
            match this with
            | Version _ -> "Prints the version of this program."
            | Sequence _ -> "Runs sequence subcommand."

[<EntryPoint>]
let main args =
    let argumentParser = ArgumentParser.Create<MainArguments>()

    try
        let arguments = argumentParser.Parse(inputs = args, raiseOnUsage = true)
        printfn "%A" arguments

    with e ->
        printfn "%s" e.Message

I expect these to throw and write out the help:

> my.exe sequence
[Sequence []]

> my.exe sequence --other test
[Sequence [Other "test"]]

Note that no arguments , --help, --version, sequence --help, and sequence --direction all work as expected:

> my.exe
[]

> my.exe --help
USAGE: ......

> my.exe --version
[Version]

> my.exe sequence --help
USAGE: ......

> my.exe sequence --direction
ERROR: argument '--direction' must be followed by <forward|backward|random>.
USAGE: ....

> my.exe sequence --direction forward
[Sequence [Direction Forward]]

Am I missing something in how I should be using the library? Or is this a bug?

jcmrva commented 1 year ago

I tried adding [<Mandatory>] to a few subcommands today and noticed it wasn't working, so I'd say this is still a bug. Using PostProcessResult makes an argument mandatory, so it's not too hard to work around.

bartelink commented 11 months ago

@jcmrva the chances are #192 will ship the fix (AFAICT it is the first nuget release that includes #127)

bartelink commented 10 months ago

@jcmrva FYI 6.1.2 is on nuget

jcmrva commented 10 months ago

Thank you!