bpaf icon indicating copy to clipboard operation
bpaf copied to clipboard

A way to prevent parsing of options after positionals

Open teohhanhui opened this issue 1 year ago • 9 comments

Perhaps related to #302, I'd like to be able to prevent parsing of options after positionals.

Given:

Usage: krun [--net=NET_MODE] [--passt-socket=PATH] COMMAND [COMMAND_ARGS]...

Available positional items:
    COMMAND                  the command you want to execute in the vm
    COMMAND_ARGS             arguments of COMMAND

Available options:
        --net=NET_MODE       Set network mode        NET_MODE can be either TSI (default) or PASST
                             [default: TSI]
        --passt-socket=PATH  Instead of starting passt, connect to passt socket at PATH
    -h, --help               Prints help information

If I do:

krun --net=PASST box64 --help

It should not be parsed as the --help option.

Sure, it's possible to just add -- but that's not always user-friendly.

teohhanhui avatar Apr 27 '24 23:04 teohhanhui

So I can better understand the problem - what's the motivation for that?

pacak avatar Apr 27 '24 23:04 pacak

As illustrated above, it's to pass all COMMAND_ARGS to COMMAND. So basically like any, but without this "problem":

“before” in the previous line means in the parser definition, not on the user input, here --turbo gets consumed by turbo parser even the argument goes

https://docs.rs/bpaf/latest/bpaf/fn.any.html#use-any-to-capture-the-remaining-arguments

teohhanhui avatar Apr 27 '24 23:04 teohhanhui

As illustrated above, it's to pass all COMMAND_ARGS to COMMAND.

I see. That's a bit tricky, my usual approach is to have different flags between inner and outer commands. To apply the restriction you want parser needs to run on arguments in order they are given on a command line, at the moment it runs in order they are specified in your parser. I need to think about an efficient way to allow this, might take some time.

pacak avatar Apr 28 '24 00:04 pacak

my usual approach is to have different flags between inner and outer commands

COMMAND in this case is just any external program. So we don't know / don't care what the arguments are.

teohhanhui avatar Apr 28 '24 00:04 teohhanhui

I have identified another (perhaps related) issue with any. I can't seem to get it to behave similarly like positional, while still consuming anything that comes after it.

To illustrate:

Given (the same program as before, but updated):

$ krun --help
Usage: krun [-e=ENV]... [--net=NET_MODE] [--passt-socket=PATH] COMMAND [COMMAND_ARGS]...

Available positional items:
    COMMAND                  the command you want to execute in the vm
    COMMAND_ARGS             arguments of COMMAND

Available options:
    -e, --env=ENV            Set environment variable to be passed to the microVM
                                     ENV should be in KEY=VALUE format, or KEY on its own to inherit the
                                     current value from the local environment
        --net=NET_MODE       Set network mode        NET_MODE can be either TSI (default) or PASST
                             [default: TSI]
        --passt-socket=PATH  Instead of starting passt, connect to passt socket at PATH
    -h, --help               Prints help information

If I do:

$ krun --net=PASST --net=PASST -- box64 --version

The second unconsumed --net=PASST ends up being consumed by any (COMMAND_ARGS). There should be a way to consume anything only from the current position, i.e. after COMMAND.

The code is here for your reference: https://github.com/teohhanhui/krun/blob/283a1f716f8c2078e9fbe6a4f2174bcba38b559e/crates/krun/src/cli_options.rs

teohhanhui avatar May 06 '24 19:05 teohhanhui

I got some ideas how to implement it and for now I'm done messing with cargo-show-asm. I'll try to make something that can solve your problem soon-ish.

pacak avatar May 06 '24 22:05 pacak

I pushed a right-adj branch that implements start_adjacent() method on parser trait. The idea is that the whole annotated block must be either directly adjacent to the beginning or to fully parsed block, so if you make it so xxx parses --net=PASST, but not box64 - parser like listed above will parse krun --net=PASST box64 --help by capturing "PASST" into xxx and ["box64", "--help"] into tail. At least for as long as xxx succeeds.

let option_parsers = xxx.start_adjacent();
let tail = any::<OsString, _, _>("TAIL", Some).many();
let parser = construct!(option_parsers, tail)

I'm planning to release a new version with it in a few days after covering some of other remaining issues.

pacak avatar Jul 02 '24 18:07 pacak

This is addressing the original issue, right? But what about the other issue I found? :see_no_evil:

teohhanhui avatar Jul 02 '24 20:07 teohhanhui

I'm looking into fixing it as well.

On Tue, Jul 2, 2024, 16:33 Teoh Han Hui @.***> wrote:

This is addressing the original issue, right? But what about the other issue I found? 🙈

— Reply to this email directly, view it on GitHub https://github.com/pacak/bpaf/issues/359#issuecomment-2204362718, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAQFI3PKKCKNBE72SYLC7TZKMFDNAVCNFSM6AAAAABG4OC75KVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDEMBUGM3DENZRHA . You are receiving this because you commented.Message ID: @.***>

pacak avatar Jul 02 '24 20:07 pacak