Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use flags as subcommands (like tar) #5042

Closed
2 tasks done
nickeb96 opened this issue Jul 23, 2023 · 4 comments
Closed
2 tasks done

Use flags as subcommands (like tar) #5042

nickeb96 opened this issue Jul 23, 2023 · 4 comments
Labels
C-enhancement Category: Raise on the bar on expectations

Comments

@nickeb96
Copy link

Please complete the following tasks

Clap Version

4.3.19

Describe your use case

I'd like to be able to build commands where there are multiple, mutually exclusive usages. A good example of this is the tar command.

From the man page:

tar {-c} [options] [files | directories]
tar {-r} -f archive-file [options] [files | directories]
tar {-t | -x} [options] [patterns]

The flags -c, -r, -t, -x change how the rest of the arguments are parsed. This would be easy to accomplish with clap if those flags were instead subcommands, but I haven't found a good way of doing it with flags.

Describe the solution you'd like

One potential solution would be to allow the presence of a flag to act as a subcommand.

Another solution would be to have a usage enum that could look something like:

#[derive(Debug, clap::Usage)]
pub enum TarUsage {
    #[usage(short_present = 'c')]
    Create(CreateArgs),
    #[usage(short_present = 'x')]
    Extract(ExtractArgs),
    #[usage(short_present = 't')]
    List(ListArgs),
}

It would dispatch parsing of the remaining arguments to a different parser based on the presence of a flag. The usage annotation could be expanded to set a default usage, or handle more complex conditions.

Alternatives, if applicable

The solution I'm using now is to just have every argument for all variants in one struct and put them all in Option<>s.

A downside of this approach is that any code using the that struct has .unwrap()s everywhere. It also requires writing a bunch of annotation rules describing when an argument is required or forbidden based on the presence of a flag.

I also need to use #[command(override_usage = ...) because the default usage string will be wrong.

I feel like this is a common type of command and the only approach I've found is not very good.

Additional Context

No response

@nickeb96 nickeb96 added the C-enhancement Category: Raise on the bar on expectations label Jul 23, 2023
@epage
Copy link
Member

epage commented Jul 24, 2023

We have subcommand long flags. Though that example hasn't been translated into the derive API, builder methods can be used as derive attributes (reference).

The one potential downside is that the subcommand names will remain visible (thought we had an issue for this but can't find it). A workaround is to use a help_template and not show the subcommands. #1334 would be useful though.

@epage
Copy link
Member

epage commented Jul 24, 2023

Alternatively, if you are willing to dispatch the positional arguments yourself, you could represent { -c | -r | { -t | -x } } as an ArgGroup and then have their respective flags requires the one from the arg group. This will fall flat if there is a flag that needs to be used differently depending on the "mode".

@nickeb96
Copy link
Author

Thanks, using #[command(short_flag = '...', long_flag = "...")] on subcommand enum variants solved my initial problem.

I was also wondering if it's possible to have a default subcommand that will be chosen when a subcommand flag isn't present.

I'd also like be able to disable the non-flag version of a subcommand so they don't conflict with positional arguments.

@epage
Copy link
Member

epage commented Jul 30, 2023

I was also wondering if it's possible to have a default subcommand that will be chosen when a subcommand flag isn't present.

Check out our git example as git stash defaults to git stash push.

I'd also like be able to disable the non-flag version of a subcommand so they don't conflict with positional arguments.

I know this has come up before but not finding a relevant issue. Feel free to create one.

Since the original answer is handled, I'm going to go ahead and close this.

@epage epage closed this as not planned Won't fix, can't repro, duplicate, stale Jul 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-enhancement Category: Raise on the bar on expectations
Projects
None yet
Development

No branches or pull requests

2 participants