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

Generate help subcommand #323

Open
Tracked by #406
ysndr opened this issue Dec 15, 2023 · 7 comments
Open
Tracked by #406

Generate help subcommand #323

ysndr opened this issue Dec 15, 2023 · 7 comments

Comments

@ysndr
Copy link
Contributor

ysndr commented Dec 15, 2023

Just a quality of life thing.
Nearly everyone using our tool tires to run prog help and prog help subcommand.
I added a subcommand myself like this:

#[derive(Debug, Bpaf, Clone)]
struct Help {
    /// Command to show help for
    #[bpaf(positional("cmd"))]
    cmd: Option<String>,
}
impl Help {
    fn handle(self) {
        let mut args = Vec::from_iter(self.cmd.as_deref());
        args.push("--help");

        match flox_cli().run_inner(&*args) {
            Ok(_) => unreachable!(),
            Err(ParseFailure::Completion(comp)) => print!("{comp}"),
            Err(ParseFailure::Stdout(doc, _)) => info!("{doc}"),
            Err(ParseFailure::Stderr(err)) => error!("{err}"),
        }
    }
}

It works, but I'd expect prog help doesnotexist to print an error.
I can't enumerate the available subcommands or anything alike so and --help permits unknowns #288 :/

Btw, are the completion and Stderr variants reachable at all if i pass just a command --help in the parser given #288?

@pacak
Copy link
Owner

pacak commented Dec 15, 2023

I can't enumerate the available subcommands

It should be possible to enumerate available commands with completion mechanism, take a look at static_complete_test_2 in tests/completion.rs. As long as you don't have anything that expands to an arbitrary text you should be able to tell what is a command by looking for items that don't start with a dash. Completion output contains lines - one per completion item with tab separated fields. Fields are: substitution, pretty substitution, group description and item help.

I need to think a bit more on making a helper for that, but it should be possible.

Btw, are the completion and Stderr variants reachable at all if i pass just a command --help in the parser given #288?

They shouldn't be produced.

@pacak
Copy link
Owner

pacak commented Dec 16, 2023

Current thoughts.

So there's two ways of solving it - adding more functionality to derive macro (in combinatorinc API it's not needed) or stabilizing some bits of the internal API.

More stuff in derive macro

Adding help subcommand is not a problem in combinatoric: you define your commands as OptionParser, then put them in a slice along with names, generate help since you have all the names, then transform all the parsers into boxed ones and combine them with choice. This should be as efficient as it can be in current implementations. A bit of allocations, nothing major. Same as using construct!([cmd1, cmd2]).

For derive - I can access command names here

#[derive(Bpaf, Debug, Clone)]
enum Opt {
    #[bpaf(command)]
    Fetch {
        dry_run: bool,
        all: bool,
        repository: String,
    },
    #[bpaf(command)]
    Add {
        interactive: bool,
        all: bool,
        files: Vec<PathBuf>,
    },
}

But not here, since external means impl Parser which can contain whatever.

#[derive(Bpaf, Debug, Clone)]
enum Opt {
    #[bpaf(external)]
    Fetch(Fetch),
    #[bpaf(external)]
    Add(Add),
}

In case where command names are visible I can use some kind of parameter that takes a way to print stuff.... Seems messy.

publishing bits of internal API

Alternatively impl Parser already gives access to public, but currently hidden function .meta() which gives access to whole parser meta info. You can tell what is optional, required, what arguments it takes, all the commands with all the help messages, etc. I can probably stabilize this along with some helper functions which means you'll have an easy way to tell what commands are there.

I think second approach is cleaner overall, need to think about this a bit longer.

@bzm3r
Copy link
Contributor

bzm3r commented Jan 22, 2024

@pacak Even the meta method implicated by Parser is not stabilized, would it be okay to make it public with a doc comment warning against unstability?

Alternatively, Parser can provide a help method which is based on meta, but is much more targeted in scope. This way, it can be stabilized, and made public, while the function it relies on (meta) can remain unstable?

@pacak
Copy link
Owner

pacak commented Jan 22, 2024

Yea, I think publishing them makes sense. It's mostly about going over the available constructors and figuring out how to document them.

@pacak pacak mentioned this issue Jan 1, 2025
24 tasks
@teohhanhui
Copy link

teohhanhui commented Jan 31, 2025

Probably relevant exploration: apple/swift-argument-parser#661

I think many tools use the myprogram mysubcommand --help form. I hope it'll be supported.

@pacak
Copy link
Owner

pacak commented Jan 31, 2025

I think many tools use the myprogram mysubcommand --help form. I hope it'll be supported

This should be supported as is. Ticket is about ability to write myprogram help to behave the same way as myprogram --help or myprogram mysubcommmand help to behave the same way as myprogram mysubcommand --help.

Probably relevant exploration:

Yup, makes sense. I'll try to support version subcommand too

@teohhanhui
Copy link

teohhanhui commented Jan 31, 2025

This should be supported as is.

My bad.

myprogram mysubcommmand help

You mean myprogram help mysubcommmand, right? Same like what cargo does.

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

No branches or pull requests

4 participants