mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 06:42:33 +00:00
refactor(HELP): A new Help Engine
The largest organizational change is that methods used to generate the help are implemented by the Help object and not the App, FlagBuilder, Parser, etc. The new code is based heavily on the old one with a few minor modifications aimed to reduce code duplication and coupling between the Help and the rest of the code. The new code turn things around: instead of having a HelpWriter that holds an AnyArg object and a method that is called with a writer as argument, there is a Help Object that holds a writer and a method that is called with a writer as an argument. There are still things to do such as moving `create_usage` outside the Parser. The peformance has been affected, probably by the use of Trait Objects. This was done as a way to reduce code duplication (i.e. in the unified help code). This performance hit should not affect the usability as generating and printing the help is dominated by user interaction and IO. The old code to generate the help is still functional and is the active one. The new code has been tested against the old one by generating help strings for most of the examples in the repo.
This commit is contained in:
parent
a91d378ba0
commit
04b5b074d1
4 changed files with 1131 additions and 0 deletions
343
benches/04_new_help.rs
Normal file
343
benches/04_new_help.rs
Normal file
|
@ -0,0 +1,343 @@
|
||||||
|
#![feature(test)]
|
||||||
|
|
||||||
|
extern crate clap;
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
use test::Bencher;
|
||||||
|
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use clap::App;
|
||||||
|
use clap::{Arg, SubCommand};
|
||||||
|
|
||||||
|
fn build_old_help(app: &App) -> String {
|
||||||
|
let mut buf = Cursor::new(Vec::with_capacity(50));
|
||||||
|
app.write_help(&mut buf).unwrap();
|
||||||
|
let content = buf.into_inner();
|
||||||
|
String::from_utf8(content).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_new_help(app: &App) -> String {
|
||||||
|
let mut buf = Cursor::new(Vec::with_capacity(50));
|
||||||
|
app.write_new_help(&mut buf).unwrap();
|
||||||
|
let content = buf.into_inner();
|
||||||
|
String::from_utf8(content).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example1<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
.version("1.0")
|
||||||
|
.author("Kevin K. <kbknapp@gmail.com>")
|
||||||
|
.about("Does awesome things")
|
||||||
|
.args_from_usage("-c, --config=[FILE] 'Sets a custom config file'
|
||||||
|
<output> 'Sets an optional output file'
|
||||||
|
-d... 'Turn debugging information on'")
|
||||||
|
.subcommand(SubCommand::with_name("test")
|
||||||
|
.about("does testing things")
|
||||||
|
.arg_from_usage("-l, --list 'lists test values'"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example2<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
.version("1.0")
|
||||||
|
.author("Kevin K. <kbknapp@gmail.com>")
|
||||||
|
.about("Does awesome things")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example3<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
// All application settings go here...
|
||||||
|
|
||||||
|
// A simple "Flag" argument example (i.e. "-d") using the builder pattern
|
||||||
|
.arg(Arg::with_name("debug")
|
||||||
|
.help("turn on debugging information")
|
||||||
|
.short("d"))
|
||||||
|
|
||||||
|
// Two arguments, one "Option" argument (i.e. one that takes a value) such
|
||||||
|
// as "-c some", and one positional argument (i.e. "myapp some_file")
|
||||||
|
.args(&[
|
||||||
|
Arg::with_name("config")
|
||||||
|
.help("sets the config file to use")
|
||||||
|
.takes_value(true)
|
||||||
|
.short("c")
|
||||||
|
.long("config"),
|
||||||
|
Arg::with_name("input")
|
||||||
|
.help("the input file to use")
|
||||||
|
.index(1)
|
||||||
|
.required(true)
|
||||||
|
])
|
||||||
|
|
||||||
|
// *Note* the following two examples are convienience methods, if you wish
|
||||||
|
// to still get the full configurability of Arg::with_name() and the readability
|
||||||
|
// of arg_from_usage(), you can instantiate a new Arg with Arg::from_usage() and
|
||||||
|
// still be able to set all the additional properties, just like Arg::with_name()
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// One "Flag" using a usage string
|
||||||
|
.arg_from_usage("--license 'display the license file'")
|
||||||
|
|
||||||
|
// Two args, one "Positional", and one "Option" using a usage string
|
||||||
|
.args_from_usage("[output] 'Supply an output file to use'
|
||||||
|
-i, --int=[IFACE] 'Set an interface to use'")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example4<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
.about("Parses an input file to do awesome things")
|
||||||
|
.version("1.0")
|
||||||
|
.author("Kevin K. <kbknapp@gmail.com>")
|
||||||
|
.arg(Arg::with_name("debug")
|
||||||
|
.help("turn on debugging information")
|
||||||
|
.short("d")
|
||||||
|
.long("debug"))
|
||||||
|
.arg(Arg::with_name("config")
|
||||||
|
.help("sets the config file to use")
|
||||||
|
.short("c")
|
||||||
|
.long("config"))
|
||||||
|
.arg(Arg::with_name("input")
|
||||||
|
.help("the input file to use")
|
||||||
|
.index(1)
|
||||||
|
.required(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example5<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
// Regular App configuration goes here...
|
||||||
|
|
||||||
|
// We'll add a flag that represents an awesome meter...
|
||||||
|
//
|
||||||
|
// I'll explain each possible setting that "flags" accept. Keep in mind
|
||||||
|
// that you DO NOT need to set each of these for every flag, only the ones
|
||||||
|
// you want for your individual case.
|
||||||
|
.arg(Arg::with_name("awesome")
|
||||||
|
.help("turns up the awesome") // Displayed when showing help info
|
||||||
|
.short("a") // Trigger this arg with "-a"
|
||||||
|
.long("awesome") // Trigger this arg with "--awesome"
|
||||||
|
.multiple(true) // This flag should allow multiple
|
||||||
|
// occurrences such as "-aaa" or "-a -a"
|
||||||
|
.requires("config") // Says, "If the user uses -a, they MUST
|
||||||
|
// also use this other 'config' arg too"
|
||||||
|
// Can also specifiy a list using
|
||||||
|
// requires_all(Vec<&str>)
|
||||||
|
.conflicts_with("output") // Opposite of requires(), says "if the
|
||||||
|
// user uses -a, they CANNOT use 'output'"
|
||||||
|
// also has a mutually_excludes_all(Vec<&str>)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example6<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
// Regular App configuration goes here...
|
||||||
|
|
||||||
|
// We'll add two positional arguments, a input file, and a config file.
|
||||||
|
//
|
||||||
|
// I'll explain each possible setting that "positionals" accept. Keep in
|
||||||
|
// mind that you DO NOT need to set each of these for every flag, only the
|
||||||
|
// ones that apply to your individual case.
|
||||||
|
.arg(Arg::with_name("input")
|
||||||
|
.help("the input file to use") // Displayed when showing help info
|
||||||
|
.index(1) // Set the order in which the user must
|
||||||
|
// specify this argument (Starts at 1)
|
||||||
|
.requires("config") // Says, "If the user uses "input", they MUST
|
||||||
|
// also use this other 'config' arg too"
|
||||||
|
// Can also specifiy a list using
|
||||||
|
// requires_all(Vec<&str>)
|
||||||
|
.conflicts_with("output") // Opposite of requires(), says "if the
|
||||||
|
// user uses -a, they CANNOT use 'output'"
|
||||||
|
// also has a mutually_excludes_all(Vec<&str>)
|
||||||
|
.required(true) // By default this argument MUST be present
|
||||||
|
// NOTE: mutual exclusions take precedence over
|
||||||
|
// required arguments
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name("config")
|
||||||
|
.help("the config file to use")
|
||||||
|
.index(2)) // Note, we do not need to specify required(true)
|
||||||
|
// if we don't want to, because "input" already
|
||||||
|
// requires "config"
|
||||||
|
// Note, we also do not need to specify requires("input")
|
||||||
|
// because requires lists are automatically two-way
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example7<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
// Regular App configuration goes here...
|
||||||
|
|
||||||
|
// Assume we an application that accepts an input file via the "-i file"
|
||||||
|
// or the "--input file" (as wel as "--input=file").
|
||||||
|
// Below every setting supported by option arguments is discussed.
|
||||||
|
// NOTE: You DO NOT need to specify each setting, only those which apply
|
||||||
|
// to your particular case.
|
||||||
|
.arg(Arg::with_name("input")
|
||||||
|
.help("the input file to use") // Displayed when showing help info
|
||||||
|
.takes_value(true) // MUST be set to true in order to be an "option" argument
|
||||||
|
.short("i") // This argument is triggered with "-i"
|
||||||
|
.long("input") // This argument is triggered with "--input"
|
||||||
|
.multiple(true) // Set to true if you wish to allow multiple occurrences
|
||||||
|
// such as "-i file -i other_file -i third_file"
|
||||||
|
.required(true) // By default this argument MUST be present
|
||||||
|
// NOTE: mutual exclusions take precedence over
|
||||||
|
// required arguments
|
||||||
|
.requires("config") // Says, "If the user uses "input", they MUST
|
||||||
|
// also use this other 'config' arg too"
|
||||||
|
// Can also specifiy a list using
|
||||||
|
// requires_all(Vec<&str>)
|
||||||
|
.conflicts_with("output") // Opposite of requires(), says "if the
|
||||||
|
// user uses -a, they CANNOT use 'output'"
|
||||||
|
// also has a conflicts_with_all(Vec<&str>)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example8<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
// Regular App configuration goes here...
|
||||||
|
|
||||||
|
// Assume we an application that accepts an input file via the "-i file"
|
||||||
|
// or the "--input file" (as wel as "--input=file").
|
||||||
|
// Below every setting supported by option arguments is discussed.
|
||||||
|
// NOTE: You DO NOT need to specify each setting, only those which apply
|
||||||
|
// to your particular case.
|
||||||
|
.arg(Arg::with_name("input")
|
||||||
|
.help("the input file to use") // Displayed when showing help info
|
||||||
|
.takes_value(true) // MUST be set to true in order to be an "option" argument
|
||||||
|
.short("i") // This argument is triggered with "-i"
|
||||||
|
.long("input") // This argument is triggered with "--input"
|
||||||
|
.multiple(true) // Set to true if you wish to allow multiple occurrences
|
||||||
|
// such as "-i file -i other_file -i third_file"
|
||||||
|
.required(true) // By default this argument MUST be present
|
||||||
|
// NOTE: mutual exclusions take precedence over
|
||||||
|
// required arguments
|
||||||
|
.requires("config") // Says, "If the user uses "input", they MUST
|
||||||
|
// also use this other 'config' arg too"
|
||||||
|
// Can also specifiy a list using
|
||||||
|
// requires_all(Vec<&str>)
|
||||||
|
.conflicts_with("output") // Opposite of requires(), says "if the
|
||||||
|
// user uses -a, they CANNOT use 'output'"
|
||||||
|
// also has a conflicts_with_all(Vec<&str>)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example10<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("myapp")
|
||||||
|
.about("does awesome things")
|
||||||
|
.arg(Arg::with_name("CONFIG")
|
||||||
|
.help("The config file to use (default is \"config.json\")")
|
||||||
|
.short("c")
|
||||||
|
.takes_value(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example1_old(b: &mut Bencher) {
|
||||||
|
let app = example1();
|
||||||
|
b.iter(|| build_old_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example1_new(b: &mut Bencher) {
|
||||||
|
let app = example1();
|
||||||
|
b.iter(|| build_new_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example2_old(b: &mut Bencher) {
|
||||||
|
let app = example2();
|
||||||
|
b.iter(|| build_old_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example2_new(b: &mut Bencher) {
|
||||||
|
let app = example2();
|
||||||
|
b.iter(|| build_new_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example3_old(b: &mut Bencher) {
|
||||||
|
let app = example3();
|
||||||
|
b.iter(|| build_old_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example3_new(b: &mut Bencher) {
|
||||||
|
let app = example3();
|
||||||
|
b.iter(|| build_new_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example4_old(b: &mut Bencher) {
|
||||||
|
let app = example4();
|
||||||
|
b.iter(|| build_old_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example4_new(b: &mut Bencher) {
|
||||||
|
let app = example4();
|
||||||
|
b.iter(|| build_new_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example5_old(b: &mut Bencher) {
|
||||||
|
let app = example5();
|
||||||
|
b.iter(|| build_old_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example5_new(b: &mut Bencher) {
|
||||||
|
let app = example5();
|
||||||
|
b.iter(|| build_new_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example6_old(b: &mut Bencher) {
|
||||||
|
let app = example6();
|
||||||
|
b.iter(|| build_old_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example6_new(b: &mut Bencher) {
|
||||||
|
let app = example6();
|
||||||
|
b.iter(|| build_new_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example7_old(b: &mut Bencher) {
|
||||||
|
let app = example7();
|
||||||
|
b.iter(|| build_old_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example7_new(b: &mut Bencher) {
|
||||||
|
let app = example7();
|
||||||
|
b.iter(|| build_new_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example8_old(b: &mut Bencher) {
|
||||||
|
let app = example8();
|
||||||
|
b.iter(|| build_old_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example8_new(b: &mut Bencher) {
|
||||||
|
let app = example8();
|
||||||
|
b.iter(|| build_new_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example10_old(b: &mut Bencher) {
|
||||||
|
let app = example10();
|
||||||
|
b.iter(|| build_old_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn example10_new(b: &mut Bencher) {
|
||||||
|
let app = example10();
|
||||||
|
b.iter(|| build_new_help(&app));
|
||||||
|
}
|
506
src/app/help.rs
Normal file
506
src/app/help.rs
Normal file
|
@ -0,0 +1,506 @@
|
||||||
|
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::cmp;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
use vec_map::VecMap;
|
||||||
|
|
||||||
|
use errors::{Error, Result as ClapResult};
|
||||||
|
|
||||||
|
use args::{AnyArg, ArgSettings, DispOrder};
|
||||||
|
use app::{App, AppSettings};
|
||||||
|
use app::parser::Parser;
|
||||||
|
|
||||||
|
use term;
|
||||||
|
|
||||||
|
const TAB: &'static str = " ";
|
||||||
|
|
||||||
|
trait ArgWithDisplay<'b, 'c>: AnyArg<'b, 'c> + Display {}
|
||||||
|
impl<'b, 'c, T> ArgWithDisplay<'b, 'c> for T where T: AnyArg<'b, 'c> + Display {}
|
||||||
|
|
||||||
|
trait ArgWithOrder<'b, 'c>: ArgWithDisplay<'b, 'c> + DispOrder {
|
||||||
|
fn as_base(&self) -> &ArgWithDisplay<'b, 'c>;
|
||||||
|
}
|
||||||
|
impl<'b, 'c, T> ArgWithOrder<'b, 'c> for T
|
||||||
|
where T: ArgWithDisplay<'b, 'c> + DispOrder
|
||||||
|
{
|
||||||
|
fn as_base(&self) -> &ArgWithDisplay<'b, 'c> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b, 'c> DispOrder for App<'b, 'c> {
|
||||||
|
fn disp_ord(&self) -> usize {
|
||||||
|
999
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Help<'a> {
|
||||||
|
writer: &'a mut Write,
|
||||||
|
next_line_help: bool,
|
||||||
|
hide_pv: bool,
|
||||||
|
term_w: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Help<'a> {
|
||||||
|
pub fn new(w: &'a mut Write, next_line_help: bool, hide_pv: bool) -> Self {
|
||||||
|
Help {
|
||||||
|
writer: w,
|
||||||
|
next_line_help: next_line_help,
|
||||||
|
hide_pv: hide_pv,
|
||||||
|
term_w: term::dimensions().map(|(w, _)| w),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_app_help(w: &'a mut Write, app: &App) -> ClapResult<()> {
|
||||||
|
let ref parser = app.p;
|
||||||
|
let nlh = parser.is_set(AppSettings::NextLineHelp);
|
||||||
|
let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp);
|
||||||
|
Self::new(w, nlh, hide_v).write_help(&parser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn as_arg_trait<'a, 'b, T: ArgWithOrder<'a, 'b>>(x: &T) -> &ArgWithOrder<'a, 'b> {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnyArg
|
||||||
|
impl<'a> Help<'a> {
|
||||||
|
fn write_args_unsorted<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
|
||||||
|
where I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>
|
||||||
|
{
|
||||||
|
let mut longest = 0;
|
||||||
|
let mut arg_v = Vec::with_capacity(10);
|
||||||
|
for arg in args.filter(|arg| {
|
||||||
|
!(arg.is_set(ArgSettings::Hidden)) || arg.is_set(ArgSettings::NextLineHelp)
|
||||||
|
}) {
|
||||||
|
if arg.longest_filter() {
|
||||||
|
longest = cmp::max(longest, arg.to_string().len());
|
||||||
|
}
|
||||||
|
if !arg.is_set(ArgSettings::Hidden) {
|
||||||
|
arg_v.push(arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for arg in arg_v {
|
||||||
|
try!(self.write_arg(arg.as_base(), longest));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_args<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
|
||||||
|
where I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>
|
||||||
|
{
|
||||||
|
let mut longest = 0;
|
||||||
|
let mut ord_m = VecMap::new();
|
||||||
|
for arg in args.filter(|arg| {
|
||||||
|
!(arg.is_set(ArgSettings::Hidden)) || arg.is_set(ArgSettings::NextLineHelp)
|
||||||
|
}) {
|
||||||
|
if arg.longest_filter() {
|
||||||
|
longest = cmp::max(longest, arg.to_string().len());
|
||||||
|
}
|
||||||
|
if !arg.is_set(ArgSettings::Hidden) {
|
||||||
|
let btm = ord_m.entry(arg.disp_ord()).or_insert(BTreeMap::new());
|
||||||
|
btm.insert(arg.name(), arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (_, btm) in ord_m.into_iter() {
|
||||||
|
for (_, arg) in btm.into_iter() {
|
||||||
|
try!(self.write_arg(arg.as_base(), longest));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_arg<'b, 'c>(&mut self,
|
||||||
|
arg: &ArgWithDisplay<'b, 'c>,
|
||||||
|
longest: usize)
|
||||||
|
-> io::Result<()> {
|
||||||
|
debugln!("fn=write_to;");
|
||||||
|
try!(self.short(arg));
|
||||||
|
try!(self.long(arg, longest));
|
||||||
|
try!(self.val(arg, longest));
|
||||||
|
try!(self.help(arg, longest));
|
||||||
|
try!(self.writer.write(b"\n"));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn short<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> io::Result<()> {
|
||||||
|
debugln!("fn=short;");
|
||||||
|
try!(write!(self.writer, "{}", TAB));
|
||||||
|
if let Some(s) = arg.short() {
|
||||||
|
write!(self.writer, "-{}", s)
|
||||||
|
} else if arg.has_switch() {
|
||||||
|
write!(self.writer, "{}", TAB)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn long<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, longest: usize) -> io::Result<()> {
|
||||||
|
debugln!("fn=long;");
|
||||||
|
if !arg.has_switch() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if arg.takes_value() {
|
||||||
|
if let Some(l) = arg.long() {
|
||||||
|
try!(write!(self.writer,
|
||||||
|
"{}--{}",
|
||||||
|
if arg.short().is_some() {
|
||||||
|
", "
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
l));
|
||||||
|
}
|
||||||
|
try!(write!(self.writer, " "));
|
||||||
|
} else {
|
||||||
|
// write_spaces! fails when using self.writer
|
||||||
|
let ref mut w = self.writer;
|
||||||
|
if let Some(l) = arg.long() {
|
||||||
|
try!(write!(w,
|
||||||
|
"{}--{}",
|
||||||
|
if arg.short().is_some() {
|
||||||
|
", "
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
l));
|
||||||
|
if !self.next_line_help || !arg.is_set(ArgSettings::NextLineHelp) {
|
||||||
|
write_spaces!((longest + 4) - (l.len() + 2), w);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !self.next_line_help || !arg.is_set(ArgSettings::NextLineHelp) {
|
||||||
|
// 6 is tab (4) + -- (2)
|
||||||
|
write_spaces!((longest + 6), w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn val<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, longest: usize) -> io::Result<()> {
|
||||||
|
debugln!("fn=val;");
|
||||||
|
if !arg.takes_value() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if let Some(ref vec) = arg.val_names() {
|
||||||
|
let mut it = vec.iter().peekable();
|
||||||
|
while let Some((_, val)) = it.next() {
|
||||||
|
try!(write!(self.writer, "<{}>", val));
|
||||||
|
if it.peek().is_some() {
|
||||||
|
try!(write!(self.writer, " "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let num = vec.len();
|
||||||
|
if arg.is_set(ArgSettings::Multiple) && num == 1 {
|
||||||
|
try!(write!(self.writer, "..."));
|
||||||
|
}
|
||||||
|
} else if let Some(num) = arg.num_vals() {
|
||||||
|
let mut it = (0..num).peekable();
|
||||||
|
while let Some(_) = it.next() {
|
||||||
|
try!(write!(self.writer, "<{}>", arg.name()));
|
||||||
|
if it.peek().is_some() {
|
||||||
|
try!(write!(self.writer, " "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try!(write!(self.writer, "{}", arg));
|
||||||
|
}
|
||||||
|
let ref mut w = self.writer;
|
||||||
|
if arg.has_switch() {
|
||||||
|
if !(self.next_line_help || arg.is_set(ArgSettings::NextLineHelp)) {
|
||||||
|
let self_len = arg.to_string().len();
|
||||||
|
// subtract ourself
|
||||||
|
let mut spcs = longest - self_len;
|
||||||
|
// Since we're writing spaces from the tab point we first need to know if we
|
||||||
|
// had a long and short, or just short
|
||||||
|
if arg.long().is_some() {
|
||||||
|
// Only account 4 after the val
|
||||||
|
spcs += 4;
|
||||||
|
} else {
|
||||||
|
// Only account for ', --' + 4 after the val
|
||||||
|
spcs += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_spaces!(spcs, w);
|
||||||
|
}
|
||||||
|
} else if !(self.next_line_help || arg.is_set(ArgSettings::NextLineHelp)) {
|
||||||
|
write_spaces!(longest + 4 - (arg.to_string().len()), w);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, longest: usize) -> io::Result<()> {
|
||||||
|
debugln!("fn=help;");
|
||||||
|
let spec_vals = self.spec_vals(arg);
|
||||||
|
let mut help = String::new();
|
||||||
|
let h = arg.help().unwrap_or("");
|
||||||
|
let spcs = if self.next_line_help || arg.is_set(ArgSettings::NextLineHelp) {
|
||||||
|
8 // "tab" + "tab"
|
||||||
|
} else {
|
||||||
|
longest + 12
|
||||||
|
};
|
||||||
|
// determine if our help fits or needs to wrap
|
||||||
|
let width = self.term_w.unwrap_or(0);
|
||||||
|
debugln!("Term width...{}", width);
|
||||||
|
let too_long = self.term_w.is_some() && (spcs + h.len() + spec_vals.len() >= width);
|
||||||
|
debugln!("Too long...{:?}", too_long);
|
||||||
|
|
||||||
|
// Is help on next line, if so newline + 2x tab
|
||||||
|
if self.next_line_help || arg.is_set(ArgSettings::NextLineHelp) {
|
||||||
|
try!(write!(self.writer, "\n{}{}", TAB, TAB));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Too long...");
|
||||||
|
if too_long {
|
||||||
|
sdebugln!("Yes");
|
||||||
|
help.push_str(h);
|
||||||
|
help.push_str(&*spec_vals);
|
||||||
|
debugln!("help: {}", help);
|
||||||
|
debugln!("help len: {}", help.len());
|
||||||
|
// Determine how many newlines we need to insert
|
||||||
|
let avail_chars = width - spcs;
|
||||||
|
debugln!("Usable space: {}", avail_chars);
|
||||||
|
let longest_w = {
|
||||||
|
let mut lw = 0;
|
||||||
|
for l in help.split(' ').map(|s| s.len()) {
|
||||||
|
if l > lw {
|
||||||
|
lw = l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lw
|
||||||
|
};
|
||||||
|
debugln!("Longest word...{}", longest_w);
|
||||||
|
debug!("Enough space...");
|
||||||
|
if longest_w < avail_chars {
|
||||||
|
sdebugln!("Yes");
|
||||||
|
let mut indices = vec![];
|
||||||
|
let mut idx = 0;
|
||||||
|
loop {
|
||||||
|
idx += avail_chars - 1;
|
||||||
|
if idx >= help.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 'a' arbitrary non space char
|
||||||
|
if help.chars().nth(idx).unwrap_or('a') != ' ' {
|
||||||
|
idx = find_idx_of_space(&*help, idx);
|
||||||
|
}
|
||||||
|
debugln!("Adding idx: {}", idx);
|
||||||
|
debugln!("At {}: {:?}", idx, help.chars().nth(idx));
|
||||||
|
indices.push(idx);
|
||||||
|
if &help[idx..].len() <= &avail_chars {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i, idx) in indices.iter().enumerate() {
|
||||||
|
debugln!("iter;i={},idx={}", i, idx);
|
||||||
|
let j = idx + (2 * i);
|
||||||
|
debugln!("removing: {}", j);
|
||||||
|
debugln!("at {}: {:?}", j, help.chars().nth(j));
|
||||||
|
help.remove(j);
|
||||||
|
help.insert(j, '{');
|
||||||
|
help.insert(j + 1, 'n');
|
||||||
|
help.insert(j + 2, '}');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sdebugln!("No");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sdebugln!("No");
|
||||||
|
}
|
||||||
|
let help = if !help.is_empty() {
|
||||||
|
&*help
|
||||||
|
} else if !spec_vals.is_empty() {
|
||||||
|
help.push_str(h);
|
||||||
|
help.push_str(&*spec_vals);
|
||||||
|
&*help
|
||||||
|
} else {
|
||||||
|
h
|
||||||
|
};
|
||||||
|
if help.contains("{n}") {
|
||||||
|
if let Some(part) = help.split("{n}").next() {
|
||||||
|
try!(write!(self.writer, "{}", part));
|
||||||
|
}
|
||||||
|
let ref mut w = self.writer;
|
||||||
|
for part in help.split("{n}").skip(1) {
|
||||||
|
try!(write!(w, "\n"));
|
||||||
|
if self.next_line_help || arg.is_set(ArgSettings::NextLineHelp) {
|
||||||
|
try!(write!(w, "{}{}", TAB, TAB));
|
||||||
|
} else if arg.has_switch() {
|
||||||
|
write_spaces!(longest + 12, w);
|
||||||
|
} else {
|
||||||
|
write_spaces!(longest + 8, w);
|
||||||
|
}
|
||||||
|
try!(write!(w, "{}", part));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try!(write!(self.writer, "{}", help));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spec_vals(&self, a: &ArgWithDisplay) -> String {
|
||||||
|
debugln!("fn=spec_vals;");
|
||||||
|
if let Some(ref pv) = a.default_val() {
|
||||||
|
debugln!("Writing defaults");
|
||||||
|
return format!(" [default: {}] {}",
|
||||||
|
pv,
|
||||||
|
if !self.hide_pv {
|
||||||
|
if let Some(ref pv) = a.possible_vals() {
|
||||||
|
format!(" [values: {}]", pv.join(", "))
|
||||||
|
} else {
|
||||||
|
"".into()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"".into()
|
||||||
|
});
|
||||||
|
} else if !self.hide_pv {
|
||||||
|
debugln!("Writing values");
|
||||||
|
if let Some(ref pv) = a.possible_vals() {
|
||||||
|
debugln!("Possible vals...{:?}", pv);
|
||||||
|
return format!(" [values: {}]", pv.join(", "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Parser
|
||||||
|
impl<'a> Help<'a> {
|
||||||
|
pub fn write_all_args(&mut self, parser: &Parser) -> ClapResult<()> {
|
||||||
|
|
||||||
|
let flags = !parser.has_flags();
|
||||||
|
let pos = !parser.has_positionals();
|
||||||
|
let opts = !parser.has_opts();
|
||||||
|
let subcmds = !parser.has_subcommands();
|
||||||
|
|
||||||
|
let unified_help = parser.is_set(AppSettings::UnifiedHelpMessage);
|
||||||
|
|
||||||
|
if unified_help && (flags || opts) {
|
||||||
|
let opts_flags = parser.iter_flags()
|
||||||
|
.map(as_arg_trait)
|
||||||
|
.chain(parser.iter_opts().map(as_arg_trait));
|
||||||
|
try!(write!(self.writer, "\nOPTIONS:\n"));
|
||||||
|
try!(self.write_args(opts_flags));
|
||||||
|
} else {
|
||||||
|
if flags {
|
||||||
|
try!(write!(self.writer, "\nFLAGS:\n"));
|
||||||
|
try!(self.write_args(parser.iter_flags()
|
||||||
|
.map(as_arg_trait)));
|
||||||
|
}
|
||||||
|
if opts {
|
||||||
|
try!(write!(self.writer, "\nOPTIONS:\n"));
|
||||||
|
try!(self.write_args(parser.iter_opts().map(as_arg_trait)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pos {
|
||||||
|
try!(write!(self.writer, "\nARGS:\n"));
|
||||||
|
try!(self.write_args_unsorted(parser.iter_positionals().map(as_arg_trait)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if subcmds {
|
||||||
|
try!(write!(self.writer, "\nSUBCOMMANDS:\n"));
|
||||||
|
|
||||||
|
let mut longest = 0;
|
||||||
|
|
||||||
|
let mut ord_m = VecMap::new();
|
||||||
|
for sc in parser.subcommands.iter().filter(|s| !s.p.is_set(AppSettings::Hidden)) {
|
||||||
|
let btm = ord_m.entry(sc.p.meta.disp_ord).or_insert(BTreeMap::new());
|
||||||
|
btm.insert(sc.p.meta.name.clone(), sc);
|
||||||
|
longest = cmp::max(longest, sc.p.meta.name.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_, btm) in ord_m.into_iter() {
|
||||||
|
for (_, sc) in btm.into_iter() {
|
||||||
|
try!(self.write_arg(sc, longest));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_version(&mut self, parser: &Parser) -> io::Result<()> {
|
||||||
|
try!(write!(self.writer, "{}", parser.meta.version.unwrap_or("".into())));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_bin_name(&mut self, parser: &Parser) -> io::Result<()> {
|
||||||
|
if let Some(bn) = parser.meta.bin_name.as_ref() {
|
||||||
|
if bn.contains(' ') {
|
||||||
|
// Incase we're dealing with subcommands i.e. git mv is translated to git-mv
|
||||||
|
try!(write!(self.writer, "{}", bn.replace(" ", "-")))
|
||||||
|
} else {
|
||||||
|
try!(write!(self.writer, "{}", &parser.meta.name[..]))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try!(write!(self.writer, "{}", &parser.meta.name[..]))
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_help(&mut self, parser: &Parser) -> ClapResult<()> {
|
||||||
|
if let Some(h) = parser.meta.help_str {
|
||||||
|
try!(writeln!(self.writer, "{}", h).map_err(Error::from));
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
self.write_default_help(&parser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "lints", allow(for_kv_map))]
|
||||||
|
pub fn write_default_help(&mut self, parser: &Parser) -> ClapResult<()> {
|
||||||
|
|
||||||
|
// Print the version
|
||||||
|
try!(self.write_bin_name(&parser));
|
||||||
|
try!(self.writer.write(b" "));
|
||||||
|
try!(self.write_version(&parser));
|
||||||
|
try!(self.writer.write(b"\n"));
|
||||||
|
if let Some(author) = parser.meta.author {
|
||||||
|
try!(write!(self.writer, "{}\n", author));
|
||||||
|
}
|
||||||
|
if let Some(about) = parser.meta.about {
|
||||||
|
try!(write!(self.writer, "{}\n", about));
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(write!(self.writer, "\n{}", parser.create_usage(&[])));
|
||||||
|
|
||||||
|
let flags = !parser.has_flags();
|
||||||
|
let pos = !parser.has_positionals();
|
||||||
|
let opts = !parser.has_opts();
|
||||||
|
let subcmds = !parser.has_subcommands();
|
||||||
|
|
||||||
|
if flags || opts || pos || subcmds {
|
||||||
|
try!(write!(self.writer, "\n"));
|
||||||
|
try!(self.write_all_args(&parser));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(h) = parser.meta.more_help {
|
||||||
|
try!(write!(self.writer, "\n{}", h));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.writer.flush().map_err(Error::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn find_idx_of_space(full: &str, start: usize) -> usize {
|
||||||
|
debugln!("fn=find_idx_of_space;");
|
||||||
|
let haystack = &full[..start];
|
||||||
|
debugln!("haystack: {}", haystack);
|
||||||
|
for (i, c) in haystack.chars().rev().enumerate() {
|
||||||
|
debugln!("iter;c={},i={}", c, i);
|
||||||
|
if c == ' ' {
|
||||||
|
debugln!("Found space returning start-i...{}", start - (i + 1));
|
||||||
|
return start - (i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ mod settings;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod meta;
|
mod meta;
|
||||||
|
mod help;
|
||||||
|
|
||||||
pub use self::settings::AppSettings;
|
pub use self::settings::AppSettings;
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ use vec_map::VecMap;
|
||||||
|
|
||||||
use args::{Arg, HelpWriter, ArgSettings, AnyArg, ArgGroup, ArgMatches, ArgMatcher};
|
use args::{Arg, HelpWriter, ArgSettings, AnyArg, ArgGroup, ArgMatches, ArgMatcher};
|
||||||
use app::parser::Parser;
|
use app::parser::Parser;
|
||||||
|
use app::help::Help;
|
||||||
use errors::Error;
|
use errors::Error;
|
||||||
use errors::Result as ClapResult;
|
use errors::Result as ClapResult;
|
||||||
|
|
||||||
|
@ -631,6 +633,21 @@ impl<'a, 'b> App<'a, 'b> {
|
||||||
self.p.write_help(w)
|
self.p.write_help(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes the full help message to the user to a `io::Write` object
|
||||||
|
/// USING A NEW implementation.
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use clap::App;
|
||||||
|
/// use std::io;
|
||||||
|
/// let mut app = App::new("myprog");
|
||||||
|
/// let mut out = io::stdout();
|
||||||
|
/// app.write_help(&mut out).ok().expect("failed to write to stdout");
|
||||||
|
/// ```
|
||||||
|
pub fn write_new_help<W: Write>(&self, w: &mut W) -> ClapResult<()> {
|
||||||
|
Help::write_app_help(w, &self)
|
||||||
|
}
|
||||||
|
|
||||||
/// Starts the parsing process, upon a failed parse an error will be displayed to the user and
|
/// Starts the parsing process, upon a failed parse an error will be displayed to the user and
|
||||||
/// the process with exit with the appropriate error code. By default this method gets matches
|
/// the process with exit with the appropriate error code. By default this method gets matches
|
||||||
/// from `env::args_os`
|
/// from `env::args_os`
|
||||||
|
|
265
tests/new_help.rs
Normal file
265
tests/new_help.rs
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
use test::Bencher;
|
||||||
|
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use clap::App;
|
||||||
|
use clap::{Arg, SubCommand};
|
||||||
|
|
||||||
|
fn build_old_help(app: &App) -> String {
|
||||||
|
let mut buf = Cursor::new(Vec::with_capacity(50));
|
||||||
|
app.write_help(&mut buf).unwrap();
|
||||||
|
let content = buf.into_inner();
|
||||||
|
String::from_utf8(content).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_new_help(app: &App) -> String {
|
||||||
|
let mut buf = Cursor::new(Vec::with_capacity(50));
|
||||||
|
app.write_new_help(&mut buf).unwrap();
|
||||||
|
let content = buf.into_inner();
|
||||||
|
String::from_utf8(content).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare(app: &App) -> bool {
|
||||||
|
let old = build_old_help(&app);
|
||||||
|
let new = build_new_help(&app);
|
||||||
|
let b = old == new;
|
||||||
|
if !b {
|
||||||
|
println!("");
|
||||||
|
println!("--> old");
|
||||||
|
println!("{}", old);
|
||||||
|
println!("--> new");
|
||||||
|
println!("{}", new);
|
||||||
|
println!("--")
|
||||||
|
}
|
||||||
|
b
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new_help() {
|
||||||
|
assert!(compare(&example1()));
|
||||||
|
assert!(compare(&example2()));
|
||||||
|
assert!(compare(&example3()));
|
||||||
|
assert!(compare(&example4()));
|
||||||
|
assert!(compare(&example5()));
|
||||||
|
assert!(compare(&example6()));
|
||||||
|
assert!(compare(&example7()));
|
||||||
|
assert!(compare(&example8()));
|
||||||
|
assert!(compare(&example10()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example1<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
.version("1.0")
|
||||||
|
.author("Kevin K. <kbknapp@gmail.com>")
|
||||||
|
.about("Does awesome things")
|
||||||
|
.args_from_usage("-c, --config=[FILE] 'Sets a custom config file'
|
||||||
|
<output> 'Sets an optional output file'
|
||||||
|
-d... 'Turn debugging information on'")
|
||||||
|
.subcommand(SubCommand::with_name("test")
|
||||||
|
.about("does testing things")
|
||||||
|
.arg_from_usage("-l, --list 'lists test values'"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example2<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
.version("1.0")
|
||||||
|
.author("Kevin K. <kbknapp@gmail.com>")
|
||||||
|
.about("Does awesome things")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example3<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
// All application settings go here...
|
||||||
|
|
||||||
|
// A simple "Flag" argument example (i.e. "-d") using the builder pattern
|
||||||
|
.arg(Arg::with_name("debug")
|
||||||
|
.help("turn on debugging information")
|
||||||
|
.short("d"))
|
||||||
|
|
||||||
|
// Two arguments, one "Option" argument (i.e. one that takes a value) such
|
||||||
|
// as "-c some", and one positional argument (i.e. "myapp some_file")
|
||||||
|
.args(&[
|
||||||
|
Arg::with_name("config")
|
||||||
|
.help("sets the config file to use")
|
||||||
|
.takes_value(true)
|
||||||
|
.short("c")
|
||||||
|
.long("config"),
|
||||||
|
Arg::with_name("input")
|
||||||
|
.help("the input file to use")
|
||||||
|
.index(1)
|
||||||
|
.required(true)
|
||||||
|
])
|
||||||
|
|
||||||
|
// *Note* the following two examples are convienience methods, if you wish
|
||||||
|
// to still get the full configurability of Arg::with_name() and the readability
|
||||||
|
// of arg_from_usage(), you can instantiate a new Arg with Arg::from_usage() and
|
||||||
|
// still be able to set all the additional properties, just like Arg::with_name()
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// One "Flag" using a usage string
|
||||||
|
.arg_from_usage("--license 'display the license file'")
|
||||||
|
|
||||||
|
// Two args, one "Positional", and one "Option" using a usage string
|
||||||
|
.args_from_usage("[output] 'Supply an output file to use'
|
||||||
|
-i, --int=[IFACE] 'Set an interface to use'")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example4<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
.about("Parses an input file to do awesome things")
|
||||||
|
.version("1.0")
|
||||||
|
.author("Kevin K. <kbknapp@gmail.com>")
|
||||||
|
.arg(Arg::with_name("debug")
|
||||||
|
.help("turn on debugging information")
|
||||||
|
.short("d")
|
||||||
|
.long("debug"))
|
||||||
|
.arg(Arg::with_name("config")
|
||||||
|
.help("sets the config file to use")
|
||||||
|
.short("c")
|
||||||
|
.long("config"))
|
||||||
|
.arg(Arg::with_name("input")
|
||||||
|
.help("the input file to use")
|
||||||
|
.index(1)
|
||||||
|
.required(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example5<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
// Regular App configuration goes here...
|
||||||
|
|
||||||
|
// We'll add a flag that represents an awesome meter...
|
||||||
|
//
|
||||||
|
// I'll explain each possible setting that "flags" accept. Keep in mind
|
||||||
|
// that you DO NOT need to set each of these for every flag, only the ones
|
||||||
|
// you want for your individual case.
|
||||||
|
.arg(Arg::with_name("awesome")
|
||||||
|
.help("turns up the awesome") // Displayed when showing help info
|
||||||
|
.short("a") // Trigger this arg with "-a"
|
||||||
|
.long("awesome") // Trigger this arg with "--awesome"
|
||||||
|
.multiple(true) // This flag should allow multiple
|
||||||
|
// occurrences such as "-aaa" or "-a -a"
|
||||||
|
.requires("config") // Says, "If the user uses -a, they MUST
|
||||||
|
// also use this other 'config' arg too"
|
||||||
|
// Can also specifiy a list using
|
||||||
|
// requires_all(Vec<&str>)
|
||||||
|
.conflicts_with("output") // Opposite of requires(), says "if the
|
||||||
|
// user uses -a, they CANNOT use 'output'"
|
||||||
|
// also has a mutually_excludes_all(Vec<&str>)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example6<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
// Regular App configuration goes here...
|
||||||
|
|
||||||
|
// We'll add two positional arguments, a input file, and a config file.
|
||||||
|
//
|
||||||
|
// I'll explain each possible setting that "positionals" accept. Keep in
|
||||||
|
// mind that you DO NOT need to set each of these for every flag, only the
|
||||||
|
// ones that apply to your individual case.
|
||||||
|
.arg(Arg::with_name("input")
|
||||||
|
.help("the input file to use") // Displayed when showing help info
|
||||||
|
.index(1) // Set the order in which the user must
|
||||||
|
// specify this argument (Starts at 1)
|
||||||
|
.requires("config") // Says, "If the user uses "input", they MUST
|
||||||
|
// also use this other 'config' arg too"
|
||||||
|
// Can also specifiy a list using
|
||||||
|
// requires_all(Vec<&str>)
|
||||||
|
.conflicts_with("output") // Opposite of requires(), says "if the
|
||||||
|
// user uses -a, they CANNOT use 'output'"
|
||||||
|
// also has a mutually_excludes_all(Vec<&str>)
|
||||||
|
.required(true) // By default this argument MUST be present
|
||||||
|
// NOTE: mutual exclusions take precedence over
|
||||||
|
// required arguments
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name("config")
|
||||||
|
.help("the config file to use")
|
||||||
|
.index(2)) // Note, we do not need to specify required(true)
|
||||||
|
// if we don't want to, because "input" already
|
||||||
|
// requires "config"
|
||||||
|
// Note, we also do not need to specify requires("input")
|
||||||
|
// because requires lists are automatically two-way
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example7<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
// Regular App configuration goes here...
|
||||||
|
|
||||||
|
// Assume we an application that accepts an input file via the "-i file"
|
||||||
|
// or the "--input file" (as wel as "--input=file").
|
||||||
|
// Below every setting supported by option arguments is discussed.
|
||||||
|
// NOTE: You DO NOT need to specify each setting, only those which apply
|
||||||
|
// to your particular case.
|
||||||
|
.arg(Arg::with_name("input")
|
||||||
|
.help("the input file to use") // Displayed when showing help info
|
||||||
|
.takes_value(true) // MUST be set to true in order to be an "option" argument
|
||||||
|
.short("i") // This argument is triggered with "-i"
|
||||||
|
.long("input") // This argument is triggered with "--input"
|
||||||
|
.multiple(true) // Set to true if you wish to allow multiple occurrences
|
||||||
|
// such as "-i file -i other_file -i third_file"
|
||||||
|
.required(true) // By default this argument MUST be present
|
||||||
|
// NOTE: mutual exclusions take precedence over
|
||||||
|
// required arguments
|
||||||
|
.requires("config") // Says, "If the user uses "input", they MUST
|
||||||
|
// also use this other 'config' arg too"
|
||||||
|
// Can also specifiy a list using
|
||||||
|
// requires_all(Vec<&str>)
|
||||||
|
.conflicts_with("output") // Opposite of requires(), says "if the
|
||||||
|
// user uses -a, they CANNOT use 'output'"
|
||||||
|
// also has a conflicts_with_all(Vec<&str>)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example8<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("MyApp")
|
||||||
|
// Regular App configuration goes here...
|
||||||
|
|
||||||
|
// Assume we an application that accepts an input file via the "-i file"
|
||||||
|
// or the "--input file" (as wel as "--input=file").
|
||||||
|
// Below every setting supported by option arguments is discussed.
|
||||||
|
// NOTE: You DO NOT need to specify each setting, only those which apply
|
||||||
|
// to your particular case.
|
||||||
|
.arg(Arg::with_name("input")
|
||||||
|
.help("the input file to use") // Displayed when showing help info
|
||||||
|
.takes_value(true) // MUST be set to true in order to be an "option" argument
|
||||||
|
.short("i") // This argument is triggered with "-i"
|
||||||
|
.long("input") // This argument is triggered with "--input"
|
||||||
|
.multiple(true) // Set to true if you wish to allow multiple occurrences
|
||||||
|
// such as "-i file -i other_file -i third_file"
|
||||||
|
.required(true) // By default this argument MUST be present
|
||||||
|
// NOTE: mutual exclusions take precedence over
|
||||||
|
// required arguments
|
||||||
|
.requires("config") // Says, "If the user uses "input", they MUST
|
||||||
|
// also use this other 'config' arg too"
|
||||||
|
// Can also specifiy a list using
|
||||||
|
// requires_all(Vec<&str>)
|
||||||
|
.conflicts_with("output") // Opposite of requires(), says "if the
|
||||||
|
// user uses -a, they CANNOT use 'output'"
|
||||||
|
// also has a conflicts_with_all(Vec<&str>)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example10<'b, 'c>() -> App<'b, 'c> {
|
||||||
|
App::new("myapp")
|
||||||
|
.about("does awesome things")
|
||||||
|
.arg(Arg::with_name("CONFIG")
|
||||||
|
.help("The config file to use (default is \"config.json\")")
|
||||||
|
.short("c")
|
||||||
|
.takes_value(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn old_example1(b: &mut Bencher) {
|
||||||
|
let app = example1();
|
||||||
|
b.iter(|| build_old_help(&app));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn new_example1(b: &mut Bencher) {
|
||||||
|
let app = example1();
|
||||||
|
b.iter(|| build_new_help(&app));
|
||||||
|
}
|
Loading…
Reference in a new issue