alecthomas / kong

Kong is a command-line parser for Go
MIT License
2.09k stars 138 forks source link

Multiple commands sharing the same positional arguments #408

Open ylacancellera opened 8 months ago

ylacancellera commented 8 months ago

Hi

Asking because my workaround now brings me too many issues

I currently have

var  CLI struct {
    CommonOption bool

    Cmd1 cmd1 `cmd`
    Cmd2 cmd2 `cmd`
}

type Cmd1 struct {
    Paths []string `arg`
    SpecificOption bool
}

type Cmd2 struct {
    Paths []string `arg`
    OtherSpecificOption bool
}

"Paths" is a required arguments shared by almost all subcommands

Commands:
  list <paths> ...

  ctx <paths> ...

  regex-list

  conflicts <paths> ...

I have tried but never succeeded in putting Paths under "CLI", instead of duplicating it on every subcommand. It would always give panic: can't mix positional arguments and branching arguments on *struct

Is there a solution to put "Paths" under CLI somehow, so that I don't have to do CLI.List.Paths or CLI.Ctx.Path, but instead do something more generic like CLI.Paths ?

Maybe the only subcommands not using Paths could be a blocker ?

Any pointer would be greatly appreciated! Excellent library btw, simple and effective

alecthomas commented 8 months ago

No that's not possible. What would that look like in practice?

ylacancellera commented 8 months ago

Here is the actual code That's the main: https://github.com/percona/percona-toolkit/blob/v3.5.7/src/go/pt-galera-log-explainer/main.go That's a subcommand: https://github.com/percona/percona-toolkit/blob/v3.5.7/src/go/pt-galera-log-explainer/list.go Other subcommand will have "Paths" as well

And in usage it look like pt-galera-log-explainer list --all *.log, pt-galera-log-explainer list --all $(find . -name *.log) or pt-galera-log-explainer conflicts *.log, where --all is only an option for list subcommand, with paths acting as a variadic argument to accept any number of files (that's the goal of this tool)

I guess that's pretty specific, so I understand I can't just do a simple CLI.Paths, but I wonder if there could be a trick by putting "Paths" in its own structure and somehow sharing it between subcommand I just want to avoid a switch on commands to handle every possible paths from every subcommands

That's what I would avoid if possible

    var paths []string
    switch kongcli.Command() {
    case "list":
        paths = CLI.List.Paths
    case "ctx":
        paths = CLI.Ctx.Paths
    case "conflicts":
        paths = CLI.Conflicts.Paths
    }
        // use paths for a common tasks before subcommand execute
ylacancellera commented 7 months ago

For the record: not sure if that's a better solution, though it's possible to use

    for _, path := range kongcli.Path {
        if path.Positional != nil && path.Positional.Name == "paths" {
            paths, ok := path.Positional.Target.Interface().([]string)
                        ....
                }
        }