pacak / bpaf

Command line parser with applicative interface
337 stars 17 forks source link

Argument Group docs can't be overridden / flattened #290

Open ysndr opened 11 months ago

ysndr commented 11 months ago

Doc comments on external argument structs can't be overridden by the using parser.

When splitting the possible arguments of a command into sub-structs, i would like to document the structs without having the documentation leak into the help message as a group help.

use bpaf::*;

/// struct level
#[derive(Debug, Bpaf)]
#[bpaf(group_help(""))] //                          << (1) no way to reset the group help here
pub struct Message {
    /// option level
    #[bpaf(long)]
    message: String,
}

#[allow(dead_code)]
#[derive(Debug, Bpaf)]
#[bpaf(options, version)]
pub struct Options {
    /// consumer level  // << (3) completely ignored :/
    #[bpaf(external(message), group_help(""))] //   << (2) sets the group help, but does not flatten the group
    message: Message,
}

fn main() {
    let parsed = options().run();
}

(1) If the group is given a doc comment, we can't override the group help via the derive attribute. Thus, there is no way here to separate "dev" docs from cli docs. I'm also not sure about formatting capability of the derive attribute, but that's beyond the issue here.

(2) We can override the group help at the consumer side. Weird, because we can only set it using the group_help attribute, not through the doc comment.

(3) doc comments for external parsers are ignored completely.

(4) neither the parser allows me to force flattening the Message group, it will always show as a message if i add a doc comment to it.

pacak commented 11 months ago

Ack. I have a yet another derive macro refactor in progress, will try to get more control to group_help.

pacak commented 7 months ago

For (3) I'm thinking about adding external_with (open to any better name ideas).

#[derive(Debug, Bpaf)]
pub struct Options {
    /// consumer level
    #[bpaf(external_with(make_message))]
    message: Message,
}

This code can expand into something like

...
let message = make_message("message", Some("consumer level"));
...

In general external_with will expect a function fn foo(field_name: &'static str, comment: Option<&'static str>) -> impl Parser<Foo> for names structs or with fn foo(field_index: usize, comment: Option<&'static str>) -> impl Parser<Foo> for tuple structs.

This should give access to doc comments on externals (but you'll need to write some stubs) as well as ability to use custom parsers (like when implementing dd) without defining a separate function for every one of them.