Skip to content
This repository has been archived by the owner on Jan 1, 2022. It is now read-only.

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

Open
2 tasks done
epage opened this issue Dec 6, 2021 · 6 comments
Open
2 tasks done

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

epage opened this issue Dec 6, 2021 · 6 comments

Comments

@epage
Copy link
Owner

epage commented Dec 6, 2021

Issue by sjackman
Friday Jul 24, 2020 at 17:53 GMT
Originally opened as clap-rs/clap#2033


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!

@epage
Copy link
Owner Author

epage commented Dec 6, 2021

Comment by CreepySkeleton
Friday Jul 24, 2020 at 20:36 GMT


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.

@epage
Copy link
Owner Author

epage commented Dec 6, 2021

Comment by sjackman
Friday Jul 24, 2020 at 22:20 GMT


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.

@epage
Copy link
Owner Author

epage commented Dec 6, 2021

Comment by pksunkara
Friday Jul 24, 2020 at 22:22 GMT


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

@epage
Copy link
Owner Author

epage commented Dec 6, 2021

Comment by sjackman
Friday Jul 24, 2020 at 23:04 GMT


@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.

@epage
Copy link
Owner Author

epage commented Dec 6, 2021

Comment by TeXitoi
Monday Jul 27, 2020 at 17:29 GMT


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

@epage
Copy link
Owner Author

epage commented Dec 6, 2021

Comment by pickfire
Sunday Aug 09, 2020 at 05:45 GMT


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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

1 participant