Conversation
|
Hm, I now think there's a fundamental limitation here... To have a perfect CLI, we might need to wrap the same option differently depending on the subcommand. And ideally we should rewrap the help message to fit the actual terminal width. AFAIK docopt does not support this. I'll explore if we could switch from docopt to clap, it does rewrapping automatically, and should be easier to compose. |
|
Looking good! If the goal here is to reduce duplication then I wonder if we may want to pursue an alternate option parsing library? It may be the case that docopt isn't the best for Cargo's purposes, but perhaps we could investigate alternate libraries like clap? IIRC that has Rust-builder-style definitions which might allow common configuration to be refactored into helper methods. |
|
I'll then look into clap then! Now I am 90% sure that it would be a proper solution. The remaining 10% are for the chance that it might be impossible to replicate current interface backward compatibly. |
Hi! So I've finally got to try the thing we have briefly discussed at RustConf: removing duplication of docopt options in different commands. This PR is a rough proof of concept to gather feedback and decide if we need this at all.
The approach here is to use hand-rolled code generation: all commands are specified in options.txt file, from which options.rs with docopt usage strings and options strutcs is generated. In options.txt, we can defined option groups and then use them to assemble full docopt description of the command.
I've decided to go with code generation and not with runtime string concatenation for the following reasons:
In some sense, it is simpler: to understand the end result, you need to look only at
options.rs, which contains usual self-describing docopt usages and structs. You don't need to understand the tricky string concatenation stuff.We can generate not only the usage strings, but also the structs with options. I think this can be extended to also generate bash completions.
This can be extended to generate structs for different option groups, so instead of
config.configure(options.flag_verbose, options.flag_quiet, &options.flag_color, options.flag_frozen, options.flag_locked)?;one can writeconfig.configure(options.color_options(), options.lockfile_options()).So that's the plan :)
Current implementation applies it only to
buildandpublishcommands, to show what's possible without creating a huge diff. Generation is done with a separate binarygenerate-options.rsand not viabuild.rsjust because it was easier to debug. However I think it maybe better to avoidbuild.rsand just to commit the generatedoptions.rstogether withoptions.txt: the generated code is supposed to be very readable, and you might want to inspect it to see if the help message is wrapped properly, for example.generate-options.rsis very rough: lots of unwraps and does not handle multiline flag descriptions :)