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

cli: allow alias after global args #309

Merged
merged 1 commit into from May 12, 2022

Conversation

martinvonz
Copy link
Owner

Our support for aliases is very naively implemented; it assumes the
alias is the first argument in argv. It therefore fails to resolve
aliases after global arguments such as --at-op.

This patch fixes that by using doing an intial parse of the arguments
with a modified definition of the CLI passed to clap. The modified
definition has only global arguments, plus an "external
subcommand". That way we can convince clap to parse the global
arguments for us and give us the alias (or real command) and further
arguments back. Thanks to @epage for the suggestion on in
clap-rs/clap#3672. The only minor problem is that it doesn't seem
possible to tell clap to parse global arguments that come after the
external subcommand. To work around that, we manually merge any parsed
global argument before the alias with any parsed global arguments
after it.

Closes #292.

Checklist

  • I have made relevant updates to CHANGELOG.md

@martinvonz martinvonz requested a review from spectral54 May 9, 2022 05:50
src/commands.rs Outdated Show resolved Hide resolved
src/commands.rs Outdated Show resolved Hide resolved
@martinvonz martinvonz force-pushed the push-3cbb0a2b3b9e4ea299ac0c758928fcda branch from 8f5d913 to c5a312b Compare May 9, 2022 17:15
@martinvonz martinvonz force-pushed the push-891168126239401fb5a8dd118a5ab10b branch 2 times, most recently from 4a10f5c to 2f48cd6 Compare May 9, 2022 17:24
@martinvonz martinvonz force-pushed the push-891168126239401fb5a8dd118a5ab10b branch 2 times, most recently from c10b9d8 to f3c4798 Compare May 10, 2022 14:38
Base automatically changed from push-3cbb0a2b3b9e4ea299ac0c758928fcda to main May 10, 2022 16:20
@martinvonz martinvonz force-pushed the push-891168126239401fb5a8dd118a5ab10b branch from f3c4798 to f77ab04 Compare May 10, 2022 16:25
src/commands.rs Outdated Show resolved Hide resolved
@martinvonz martinvonz force-pushed the push-891168126239401fb5a8dd118a5ab10b branch from f77ab04 to e939327 Compare May 11, 2022 13:38
@martinvonz martinvonz enabled auto-merge (rebase) May 11, 2022 13:54
src/commands.rs Outdated Show resolved Hide resolved
@martinvonz martinvonz force-pushed the push-891168126239401fb5a8dd118a5ab10b branch from e939327 to c3bb559 Compare May 11, 2022 15:19
Copy link
Collaborator

@spectral54 spectral54 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: description says "That make clap", should be "makes"; also says 'suggestion on in' should just be 'suggestion in'.

Thoughts on implementation from offline discussion: Assuming that alias_args is a subvec (the tail elements of string_args), let's do something where each loop pass concatenates the first N arguments from string_args, the arguments from the alias resolution, and all of the arguments from alias_args[1..] (where N = string_args.length() - alias_args.length()).

Our support for aliases is very naively implemented; it assumes the
alias is the first argument in argv. It therefore fails to resolve
aliases after global arguments such as `--at-op`.

This patch fixes that by modifying the command defintion to have an
"external subcommand" in the list of available commands. That makes
`clap` give us the remainder of the arguments when it runs into an
unknown command. The first in the list will then be an alias or simply
an unknown command. Thanks to @epage for the suggestion on in
clap-rs/clap#3672.

With the new structure, it was easy to handle recursive alias
definitions, so I added support for that too.

Closes #292.
@martinvonz martinvonz force-pushed the push-891168126239401fb5a8dd118a5ab10b branch from c3bb559 to 628677f Compare May 11, 2022 22:09
@martinvonz
Copy link
Owner Author

Nit: description says "That make clap", should be "makes"; also says 'suggestion on in' should just be 'suggestion in'.

Done.

Thoughts on implementation from offline discussion: Assuming that alias_args is a subvec (the tail elements of string_args), let's do something where each loop pass concatenates the first N arguments from string_args, the arguments from the alias resolution, and all of the arguments from alias_args[1..] (where N = string_args.length() - alias_args.length()).

That turned out much simpler, and obviously more reliable because we don't need to manually merge earlier global arguments with later ones. @epage, can you confirm if that assumption is correct (i.e. that the list of strings passed to an external_subcommand exactly matches the end of the full list of arguments passed to parse_from())?

@epage
Copy link

epage commented May 11, 2022

That turned out much simpler, and obviously more reliable because we don't need to manually merge earlier global arguments with later ones. @epage, can you confirm if that assumption is correct (i.e. that the list of strings passed to an external_subcommand exactly matches the end of the full list of arguments passed to parse_from())?

clap does no post-processing on the external subcommand arguments; they are exactly what clap received.

If I'm understanding the suggestion, its to manually extract the global args and subcommand and merge the global args in with the external subcommand arguments. I personally wouldn't do this as I'd be concerned about it being brittle; it having corner cases to deal with.

@martinvonz
Copy link
Owner Author

That turned out much simpler, and obviously more reliable because we don't need to manually merge earlier global arguments with later ones. @epage, can you confirm if that assumption is correct (i.e. that the list of strings passed to an external_subcommand exactly matches the end of the full list of arguments passed to parse_from())?

clap does no post-processing on the external subcommand arguments; they are exactly what clap received.

Okay, that's good, and what we assumed.

If I'm understanding the suggestion, its to manually extract the global args and subcommand and merge the global args in with the external subcommand arguments. I personally wouldn't do this as I'd be concerned about it being brittle; it having corner cases to deal with.

Yes, that's correct. It actually seems much simpler and less brittle to me. See how much simpler it got.

@epage
Copy link

epage commented May 12, 2022

Yes, that's correct. It actually seems much simpler and less brittle to me. See how much simpler it got.

Fewer lines of code is not the sample thing as simplicity.

There are more assumptions on behavior with the new approach which is where my concern with brittleness comes from.

If it works for you, go for it. Like I said, for myself, I wouldn't but there are times when people come to different conclusions and that is fine.

@martinvonz
Copy link
Owner Author

Yes, that's correct. It actually seems much simpler and less brittle to me. See how much simpler it got.

Fewer lines of code is not the sample thing as simplicity.

There are more assumptions on behavior with the new approach which is where my concern with brittleness comes from.

If it works for you, go for it. Like I said, for myself, I wouldn't but there are times when people come to different conclusions and that is fine.

In what way do you think it's brittle? What should we watch out for if we do it this way? Is the concern that clap might do some parsing of the later arguments in the future (making that assertion I added fail)?

My biggest concern with the way we did it before was that we had to replicate clap's override behavior.

@epage
Copy link

epage commented May 12, 2022

I'm more going more from gut feel and do not have the time or interest in litigating this.

Copy link
Collaborator

@yuja yuja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think new implementation is better.
It's clearer that alias expansion works at lower args: &[str] layer (e.g. aliased_diff = ["diff", "-r"] is valid even though -r takes value), not at the layer where the structs parsed by clap should live (e.g. where the alias would be described as ["diff", "-r", "$1"].)

@martinvonz martinvonz merged commit 788831f into main May 12, 2022
@martinvonz martinvonz deleted the push-891168126239401fb5a8dd118a5ab10b branch May 12, 2022 13:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve support for command aliases
4 participants