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

Expand template macros found in Rust comments [feature request] #2033

Open
2 tasks done
sjackman opened this issue Jul 24, 2020 · 7 comments
Open
2 tasks done

Expand template macros found in Rust comments [feature request] #2033

sjackman opened this issue Jul 24, 2020 · 7 comments
Labels
A-help Area: documentation, including docs.rs, readme, examples, etc... C-enhancement Category: Raise on the bar on expectations S-waiting-on-decision Status: Waiting on a go/no-go before implementing

Comments

@sjackman
Copy link

Make sure you completed the following tasks

Describe your use case

I'm using the clap with its derive macros. The same library code is used with two separate executables. I'd like to include the name of the executable in the about text of a command line argument. For example.

Describe the solution you'd like

    /// Path to the JSON file produced by '{bin} clean --out=JSON INPUT.TXT'
    #[clap(long = "json")]
    pub json: CliPath,

where {bin} is the name of one of the two executables that uses this library code.

Alternatives, if applicable

We could convert the program to use builder notation rather than the derive macros, which would make it easier to programmatically format the text of an about option. All of our projects use the derive macros though, so this would be a deviation from our other projects. Is either the YAML or macro mode appropriate?

Additional context

Thank you very much for Clap! It's fantastic!

@CreepySkeleton
Copy link
Contributor

I think this is one of those feature requests which are, while unquestionably useful, very hard to design and implement properly.

Why derive-only won't do

For starters, this cannot be derive macro only feature because

  • The binary name (for example) is unknown until runtime, and #[derive(Clap)] works at "macro expansion time". There's no {bin} we could replace the placeholder with, or rather, it's to be determined when you actually run the program (aliases and stuff). The derive code doesn't have the data simply because the data... doesn't exist yet.
  • I see people often make the same mistake when they talk about derive macros - they don't understand that "one #[derive], one expansion". You want to reuse the generated code in two different places (you wrote about two executables), but you expect the generated code to differ (you want different help messages). It won't.

Alternative proposal

Build it in clap itself:

Arg::new("json")
    .long("json")
    .help("Path to the JSON file produced by '{bin} clean --out=JSON INPUT.TXT'")

Then clap scans the help messages at runtime seeking for {placeholder}, replaces it, and prints it (or does whatever was requested).

List of placeholders

Anything other than {bin}? I would like a list here.

Runtime overhead

If this is implemented, clap will have to scan through all help messages at runtime. Good news is, the scanning will run only when generating help messages, and that ain't the part you expect to be amazingly fast. We may run the scan step only if AppSettings::HelpPlaceholders is set, but I don't think it worth expanding public API.

@CreepySkeleton CreepySkeleton added A-help Area: documentation, including docs.rs, readme, examples, etc... E-hard Call for participation: Experience needed to fix: Hard / a lot P4: nice to have labels Jul 24, 2020
@sjackman
Copy link
Author

If this is implemented, clap will have to scan through all help messages at runtime. Good news is, the scanning will run only when generating help messages, and that ain't the part you expect to be amazingly fast.

Yes, I agree that the expansion would happen at run time.
Thanks for your quick response!

I specifically want this feature when using Clap with its derive macros. When using the builder pattern, format! can easily be used.

Arg::new("json")
    .long("json")
    .help(format!("Path to the JSON file produced by '{} clean --out=JSON INPUT.TXT'"), bin_name)

When using the derive macro

    /// Path to the JSON file produced by '{bin} clean --out=JSON INPUT.TXT'
    #[clap(long = "json")]
    pub json: CliPath,

the expansion of {bin} would be done at run time when the user invokes command --help.

pub fn get_help<T: IntoApp>() -> String {
    let mut output = Vec::new();
    <T as IntoApp>::into_app().write_help(&mut output).unwrap();
-   let output = String::from_utf8(output).unwrap();
+   let output = String::from_utf8(output).unwrap().replace("{bin}", self.get_bin_name()?);

    eprintln!("\n%%% HELP %%%:=====\n{}\n=====\n", output);
    eprintln!("\n%%% HELP (DEBUG) %%%:=====\n{:?}\n=====\n", output);

    output
}

Anything other than {bin}? I would like a list here.

https://docs.rs/clap/2.33.1/clap/struct.App.html#method.template

I looked through the other template macros, and {bin} seemed like the only one that would be useful to me.

@pksunkara
Copy link
Member

You should be able to use format! with derive macros too since doc strings are just syntactic sugar for #[doc = ""]

@sjackman
Copy link
Author

@CreepySkeleton pointed out above…

The binary name (for example) is unknown until runtime, and #[derive(Clap)] works at "macro expansion time". There's no {bin} we could replace the placeholder with, or rather, it's to be determined when you actually run the program (aliases and stuff). The derive code doesn't have the data simply because the data... doesn't exist yet.

@TeXitoi
Copy link
Contributor

TeXitoi commented Jul 27, 2020

That's not totally true: the code is generated at expantiin time, but the given string can be generated at run time. See the lazy_static hack

@pickfire
Copy link
Contributor

pickfire commented Aug 9, 2020

Templating doc comments is a bit hard even when using a macro but there is a tool on that https://github.com/dtolnay/paste. Not sure if this is useful.

@epage epage added C-enhancement Category: Raise on the bar on expectations and removed T: new feature labels Dec 8, 2021
@epage
Copy link
Member

epage commented Dec 10, 2021

Agreed that we'd want to implement this feature in the Builder for the Derive API to use.

#2914's idea for code-genning help messages would open the door for us doing this without incurring runtime costs.

@epage epage added S-waiting-on-decision Status: Waiting on a go/no-go before implementing and removed P4: nice to have E-hard Call for participation: Experience needed to fix: Hard / a lot labels Dec 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-help Area: documentation, including docs.rs, readme, examples, etc... C-enhancement Category: Raise on the bar on expectations S-waiting-on-decision Status: Waiting on a go/no-go before implementing
Projects
None yet
Development

No branches or pull requests

6 participants