From 066df7486e684cf50a8479a356a12ba972c34ce1 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Wed, 17 Feb 2016 02:54:25 -0500 Subject: [PATCH 1/8] imp(Help): adds setting for next line help by arg Adds a setting for both `AppSettings` and an `Arg` method which allows placing the help text for a particular argument (or all arguments via the `AppSettings`) on the line after the argument itself and indented once. This is useful for when a single argument may have a very long invocation flag, or many value names which throws off the alignment of al other arguments. On a small terminal this can be terrible. Placing the text on the line below the argument solves this issue. This is also how many of the GNU utilities display their help strings for individual arguments. The final caes where this can be hepful is if the argument has a very long or detailed and complex help string that will span multiple lines. It can be visually more appealing and easier to read when those lines don't wrap as frequently since there is more space on the next line. Closes #427 --- src/app/parser.rs | 17 ++-- src/app/settings.rs | 60 +++++++++----- src/args/arg.rs | 52 ++++++++++++ src/args/arg_builder/flag.rs | 35 +------- src/args/arg_builder/macros.rs | 126 +++++++++++++++++++++++++++++ src/args/arg_builder/mod.rs | 2 + src/args/arg_builder/option.rs | 71 +--------------- src/args/arg_builder/positional.rs | 29 +------ src/args/settings.rs | 21 +++-- 9 files changed, 246 insertions(+), 167 deletions(-) create mode 100644 src/args/arg_builder/macros.rs diff --git a/src/app/parser.rs b/src/app/parser.rs index a4a5e9e672e..4a9deb2d469 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -1400,7 +1400,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { let mut longest_flag = 0; for fl in self.flags.iter() - .filter(|f| f.long.is_some() && !f.settings.is_set(ArgSettings::Hidden)) + .filter(|f| f.long.is_some() && !(f.settings.is_set(ArgSettings::Hidden) || f.settings.is_set(ArgSettings::NextLineHelp))) .map(|a| a.to_string().len()) { if fl > longest_flag { longest_flag = fl; @@ -1408,7 +1408,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { } let mut longest_opt = 0; for ol in self.opts.iter() - .filter(|o| !o.settings.is_set(ArgSettings::Hidden)) + .filter(|o| !(o.settings.is_set(ArgSettings::Hidden) || o.settings.is_set(ArgSettings::NextLineHelp))) .map(|a| a.to_string().len()) { if ol > longest_opt { longest_opt = ol; @@ -1417,7 +1417,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { let mut longest_pos = 0; for pl in self.positionals .values() - .filter(|p| !p.settings.is_set(ArgSettings::Hidden)) + .filter(|p| !(p.settings.is_set(ArgSettings::Hidden) || p.settings.is_set(ArgSettings::NextLineHelp))) .map(|f| f.to_string().len()) { if pl > longest_pos { longest_pos = pl; @@ -1443,17 +1443,18 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { } else { longest_opt }; + let nlh = self.settings.is_set(AppSettings::NextLineHelp); if unified_help && (flags || opts) { try!(write!(w, "\nOPTIONS:\n")); let mut combined = BTreeMap::new(); for f in self.flags.iter().filter(|f| !f.settings.is_set(ArgSettings::Hidden)) { let mut v = vec![]; - try!(f.write_help(&mut v, tab, longest)); + try!(f.write_help(&mut v, tab, longest, nlh)); combined.insert(f.name, v); } for o in self.opts.iter().filter(|o| !o.settings.is_set(ArgSettings::Hidden)) { let mut v = vec![]; - try!(o.write_help(&mut v, tab, longest, self.is_set(AppSettings::HidePossibleValuesInHelp))); + try!(o.write_help(&mut v, tab, longest, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh)); combined.insert(o.name, v); } for (_, a) in combined { @@ -1468,7 +1469,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { .map(|f| (f.name, f)) .collect::>() .values() { - try!(f.write_help(w, tab, longest)); + try!(f.write_help(w, tab, longest, nlh)); } } if opts { @@ -1478,7 +1479,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { .map(|o| (o.name, o)) .collect::>() .values() { - try!(o.write_help(w, tab, longest_opt, self.is_set(AppSettings::HidePossibleValuesInHelp))); + try!(o.write_help(w, tab, longest_opt, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh)); } } } @@ -1486,7 +1487,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { try!(write!(w, "\nARGS:\n")); for v in self.positionals.values() .filter(|p| !p.settings.is_set(ArgSettings::Hidden)) { - try!(v.write_help(w, tab, longest_pos, self.is_set(AppSettings::HidePossibleValuesInHelp))); + try!(v.write_help(w, tab, longest_pos, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh)); } } if subcmds { diff --git a/src/app/settings.rs b/src/app/settings.rs index ec30b80ed67..352272ac027 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -3,26 +3,27 @@ use std::ascii::AsciiExt; bitflags! { flags Flags: u32 { - const SC_NEGATE_REQS = 0b00000000000000000001, - const SC_REQUIRED = 0b00000000000000000010, - const A_REQUIRED_ELSE_HELP = 0b00000000000000000100, - const GLOBAL_VERSION = 0b00000000000000001000, - const VERSIONLESS_SC = 0b00000000000000010000, - const UNIFIED_HELP = 0b00000000000000100000, - const WAIT_ON_ERROR = 0b00000000000001000000, - const SC_REQUIRED_ELSE_HELP= 0b00000000000010000000, - const NEEDS_LONG_HELP = 0b00000000000100000000, - const NEEDS_LONG_VERSION = 0b00000000001000000000, - const NEEDS_SC_HELP = 0b00000000010000000000, - const DISABLE_VERSION = 0b00000000100000000000, - const HIDDEN = 0b00000001000000000000, - const TRAILING_VARARG = 0b00000010000000000000, - const NO_BIN_NAME = 0b00000100000000000000, - const ALLOW_UNK_SC = 0b00001000000000000000, - const UTF8_STRICT = 0b00010000000000000000, - const UTF8_NONE = 0b00100000000000000000, - const LEADING_HYPHEN = 0b01000000000000000000, - const NO_POS_VALUES = 0b10000000000000000000, + const SC_NEGATE_REQS = 0b000000000000000000001, + const SC_REQUIRED = 0b000000000000000000010, + const A_REQUIRED_ELSE_HELP = 0b000000000000000000100, + const GLOBAL_VERSION = 0b000000000000000001000, + const VERSIONLESS_SC = 0b000000000000000010000, + const UNIFIED_HELP = 0b000000000000000100000, + const WAIT_ON_ERROR = 0b000000000000001000000, + const SC_REQUIRED_ELSE_HELP= 0b000000000000010000000, + const NEEDS_LONG_HELP = 0b000000000000100000000, + const NEEDS_LONG_VERSION = 0b000000000001000000000, + const NEEDS_SC_HELP = 0b000000000010000000000, + const DISABLE_VERSION = 0b000000000100000000000, + const HIDDEN = 0b000000001000000000000, + const TRAILING_VARARG = 0b000000010000000000000, + const NO_BIN_NAME = 0b000000100000000000000, + const ALLOW_UNK_SC = 0b000001000000000000000, + const UTF8_STRICT = 0b000010000000000000000, + const UTF8_NONE = 0b000100000000000000000, + const LEADING_HYPHEN = 0b001000000000000000000, + const NO_POS_VALUES = 0b010000000000000000000, + const NEXT_LINE_HELP = 0b100000000000000000000, } } @@ -55,7 +56,8 @@ impl AppFlags { StrictUtf8 => UTF8_STRICT, AllowInvalidUtf8 => UTF8_NONE, AllowLeadingHyphen => LEADING_HYPHEN, - HidePossibleValuesInHelp => NO_POS_VALUES + HidePossibleValuesInHelp => NO_POS_VALUES, + NextLineHelp => NEXT_LINE_HELP } } @@ -398,9 +400,22 @@ pub enum AppSettings { /// # ; /// ``` AllowLeadingHyphen, - /// Tells `clap` *not* to print possible values when displaying help information. This can be + /// Tells `clap` *not* to print possible values when displaying help information. This can be /// useful if there are many values, or they are explained elsewhere. HidePossibleValuesInHelp, + /// Places the help string for all arguments on the line after the argument + /// + /// **NOTE:** This setting is cosmetic only and does not affect any functionality. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{App, Arg, SubCommand, AppSettings}; + /// App::new("myprog") + /// .setting(AppSettings::NextLineHelp) + /// .get_matches(); + /// ``` + NextLineHelp, #[doc(hidden)] NeedsLongVersion, #[doc(hidden)] @@ -431,6 +446,7 @@ impl FromStr for AppSettings { "allowinvalidutf8" => Ok(AppSettings::AllowInvalidUtf8), "allowleadinghyphen" => Ok(AppSettings::AllowLeadingHyphen), "hidepossiblevaluesinhelp" => Ok(AppSettings::HidePossibleValuesInHelp), + "nextlinehelp" => Ok(AppSettings::NextLineHelp), _ => Err("unknown AppSetting, cannot convert from str".to_owned()), } } diff --git a/src/args/arg.rs b/src/args/arg.rs index e8d24b8b738..c8385464615 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -1124,6 +1124,58 @@ impl<'a, 'b> Arg<'a, 'b> { self } + /// When set to `true` the help string will be displayed on the line after the argument and + /// indented once. This can be helpful for arguments with very long or complex help messages. + /// This can also be helpful for arguments with very long flag names, or many/long value names. + /// + /// **NOTE:** To apply this setting to all arguments consider using `AppSettings::NextLineHelp` + /// + /// # Examples + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("nlh") + /// .arg(Arg::with_name("opt") + /// .long("long-option-flag") + /// .short("o") + /// .takes_value(true) + /// .value_names(&["value1", "value2"]) + /// .help("Some really long help and complex{n}\ + /// help that makes more sense to be{n}\ + /// on a line after the option") + /// .next_line_help(true)) + /// .get_matches_from(vec![ + /// "nlh", "--help" + /// ]); + /// ``` + /// + /// The above example displays the following help message + /// + /// ```ignore + /// nlh + /// + /// USAGE: + /// nlh [FLAGS] [OPTIONS] + /// + /// FLAGS: + /// -h, --help Prints help information + /// -V, --version Prints version information + /// + /// OPTIONS: + /// -o, --long-option-flag + /// Some really long help and complex + /// help that makes more sense to be + /// on a line after the option + /// ``` + pub fn next_line_help(mut self, nlh: bool) -> Self { + if nlh { + self.setb(ArgSettings::NextLineHelp); + } else { + self.unsetb(ArgSettings::NextLineHelp); + } + self + } + /// Checks if one of the `ArgSettings` settings is set for the argument pub fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(s) diff --git a/src/args/arg_builder/flag.rs b/src/args/arg_builder/flag.rs index 7fce3e7923e..e22d34b18fe 100644 --- a/src/args/arg_builder/flag.rs +++ b/src/args/arg_builder/flag.rs @@ -45,39 +45,8 @@ impl<'n, 'e> FlagBuilder<'n, 'e> { } } - pub fn write_help(&self, w: &mut W, tab: &str, longest: usize) -> io::Result<()> { - try!(write!(w, "{}", tab)); - if let Some(s) = self.short { - try!(write!(w, "-{}", s)); - } else { - try!(write!(w, "{}", tab)); - } - if let Some(l) = self.long { - try!(write!(w, - "{}--{}", - if self.short.is_some() { - ", " - } else { - "" - }, - l)); - write_spaces!((longest + 4) - (l.len() + 2), w); - } else { - // 6 is tab (4) + -- (2) - write_spaces!((longest + 6), w); - } - if let Some(h) = self.help { - if h.contains("{n}") { - let mut hel = h.split("{n}"); - while let Some(part) = hel.next() { - try!(write!(w, "{}\n", part)); - write_spaces!((longest + 12), w); - try!(write!(w, "{}", hel.next().unwrap_or(""))); - } - } else { - try!(write!(w, "{}", h)); - } - } + pub fn write_help(&self, w: &mut W, tab: &str, longest: usize, nlh: bool) -> io::Result<()> { + write_arg_help!(@flag self, w, tab, longest, nlh); write!(w, "\n") } } diff --git a/src/args/arg_builder/macros.rs b/src/args/arg_builder/macros.rs new file mode 100644 index 00000000000..d641930814e --- /dev/null +++ b/src/args/arg_builder/macros.rs @@ -0,0 +1,126 @@ +macro_rules! write_arg_help { + (@opt $_self:ident, $w:ident, $tab:ident, $longest:ident, $skip_pv:ident, $nlh:ident) => { + write_arg_help!(@short $_self, $w, $tab); + write_arg_help!(@opt_long $_self, $w, $nlh, $longest); + write_arg_help!(@val $_self, $w); + if !($nlh || $_self.settings.is_set(ArgSettings::NextLineHelp)) { + write_spaces!(if $_self.long.is_some() { $longest + 4 } else { $longest + 8 } - ($_self.to_string().len()), $w); + } + if let Some(h) = $_self.help { + write_arg_help!(@help $_self, $w, h, $tab, $longest, $nlh); + write_arg_help!(@spec_vals $_self, $w, $skip_pv); + } + }; + (@flag $_self:ident, $w:ident, $tab:ident, $longest:ident, $nlh:ident) => { + write_arg_help!(@short $_self, $w, $tab); + write_arg_help!(@flag_long $_self, $w, $longest, $nlh); + if let Some(h) = $_self.help { + write_arg_help!(@help $_self, $w, h, $tab, $longest, $nlh); + } + }; + (@pos $_self:ident, $w:ident, $tab:ident, $longest:ident, $skip_pv:ident, $nlh:ident) => { + try!(write!($w, "{}", $tab)); + write_arg_help!(@val $_self, $w); + if !($nlh || $_self.settings.is_set(ArgSettings::NextLineHelp)) { + write_spaces!($longest + 4 - ($_self.to_string().len()), $w); + } + if let Some(h) = $_self.help { + write_arg_help!(@help $_self, $w, h, $tab, $longest, $nlh); + write_arg_help!(@spec_vals $_self, $w, $skip_pv); + } + }; + (@short $_self:ident, $w:ident, $tab:ident) => { + try!(write!($w, "{}", $tab)); + if let Some(s) = $_self.short { + try!(write!($w, "-{}", s)); + } else { + try!(write!($w, "{}", $tab)); + } + }; + (@flag_long $_self:ident, $w:ident, $longest:ident, $nlh:ident) => { + if let Some(l) = $_self.long { + write_arg_help!(@long $_self, $w, l); + if !$nlh || !$_self.settings.is_set(ArgSettings::NextLineHelp) { + write_spaces!(($longest + 4) - (l.len() + 2), $w); + } + } else { + if !$nlh || !$_self.settings.is_set(ArgSettings::NextLineHelp) { + // 6 is tab (4) + -- (2) + write_spaces!(($longest + 6), $w); + } + } + }; + (@opt_long $_self:ident, $w:ident, $nlh:ident, $longest:ident) => { + if let Some(l) = $_self.long { + write_arg_help!(@long $_self, $w, l); + } + try!(write!($w, " ")); + }; + (@long $_self:ident, $w:ident, $l:ident) => { + try!(write!($w, + "{}--{}", + if $_self.short.is_some() { + ", " + } else { + "" + }, + $l)); + }; + (@val $_self:ident, $w:ident) => { + if let Some(ref vec) = $_self.val_names { + let mut it = vec.iter().peekable(); + while let Some((_, val)) = it.next() { + try!(write!($w, "<{}>", val)); + if it.peek().is_some() { try!(write!($w, " ")); } + } + let num = vec.len(); + if $_self.settings.is_set(ArgSettings::Multiple) && num == 1 { + try!(write!($w, "...")); + } + } else if let Some(num) = $_self.num_vals { + for _ in 0..num { + try!(write!($w, "<{}>", $_self.name)); + } + } else { + try!(write!($w, + "<{}>{}", + $_self.name, + if $_self.settings.is_set(ArgSettings::Multiple) { + "..." + } else { + "" + })); + } + }; + (@spec_vals $_self:ident, $w:ident, $skip_pv:ident) => { + if let Some(ref pv) = $_self.default_val { + try!(write!($w, " [default: {}]", pv)); + } + if !$skip_pv { + if let Some(ref pv) = $_self.possible_vals { + try!(write!($w, " [values: {}]", pv.join(", "))); + } + } + }; + (@help $_self:ident, $w:ident, $h:ident, $tab:ident, $longest:expr, $nlh:ident) => { + if $nlh || $_self.settings.is_set(ArgSettings::NextLineHelp) { + try!(write!($w, "\n{}{}", $tab, $tab)); + } + if $h.contains("{n}") { + if let Some(part) = $h.split("{n}").next() { + try!(write!($w, "{}", part)); + } + for part in $h.split("{n}").skip(1) { + try!(write!($w, "\n")); + if $nlh || $_self.settings.is_set(ArgSettings::NextLineHelp) { + try!(write!($w, "{}{}", $tab, $tab)); + } else { + write_spaces!($longest + 12, $w); + } + try!(write!($w, "{}", part)); + } + } else { + try!(write!($w, "{}", $h)); + } + }; +} diff --git a/src/args/arg_builder/mod.rs b/src/args/arg_builder/mod.rs index 2f96a5c7818..eafca0289b6 100644 --- a/src/args/arg_builder/mod.rs +++ b/src/args/arg_builder/mod.rs @@ -2,6 +2,8 @@ pub use self::flag::FlagBuilder; pub use self::option::OptBuilder; pub use self::positional::PosBuilder; +#[macro_use] +mod macros; #[allow(dead_code)] mod flag; #[allow(dead_code)] diff --git a/src/args/arg_builder/option.rs b/src/args/arg_builder/option.rs index 5ad1aedaf25..a2bf0c7558f 100644 --- a/src/args/arg_builder/option.rs +++ b/src/args/arg_builder/option.rs @@ -101,76 +101,9 @@ impl<'n, 'e> OptBuilder<'n, 'e> { ob } - pub fn write_help(&self, w: &mut W, tab: &str, longest: usize, skip_pv: bool) -> io::Result<()> { + pub fn write_help(&self, w: &mut W, tab: &str, longest: usize, skip_pv: bool, nlh: bool) -> io::Result<()> { debugln!("fn=write_help"); - // if it supports multiple we add '...' i.e. 3 to the name length - try!(write!(w, "{}", tab)); - if let Some(s) = self.short { - try!(write!(w, "-{}", s)); - } else { - try!(write!(w, "{}", tab)); - } - if let Some(l) = self.long { - try!(write!(w, - "{}--{}", - if self.short.is_some() { - ", " - } else { - "" - }, - l)); - } - if let Some(ref vec) = self.val_names { - for (_, val) in vec { - try!(write!(w, " <{}>", val)); - } - let num = vec.len(); - if self.settings.is_set(ArgSettings::Multiple) && num == 1 { - try!(write!(w, "...")); - } - } else if let Some(num) = self.num_vals { - for _ in 0..num { - try!(write!(w, " <{}>", self.name)); - } - } else { - try!(write!(w, - " <{}>{}", - self.name, - if self.settings.is_set(ArgSettings::Multiple) { - "..." - } else { - "" - })); - } - if self.long.is_some() { - write_spaces!((longest + 4) - (self.to_string().len()), w); - } else { - // 8 = tab + '-a, '.len() - write_spaces!((longest + 8) - (self.to_string().len()), w); - } - if let Some(h) = self.help { - if h.contains("{n}") { - let mut hel = h.split("{n}"); - if let Some(part) = hel.next() { - try!(write!(w, "{}", part)); - } - for part in hel { - try!(write!(w, "\n")); - write_spaces!(longest + 12, w); - try!(write!(w, "{}", part)); - } - } else { - try!(write!(w, "{}", h)); - } - if let Some(ref pv) = self.default_val { - try!(write!(w, " [default: {}]", pv)); - } - if !skip_pv { - if let Some(ref pv) = self.possible_vals { - try!(write!(w, " [values: {}]", pv.join(", "))); - } - } - } + write_arg_help!(@opt self, w, tab, longest, skip_pv, nlh); write!(w, "\n") } } diff --git a/src/args/arg_builder/positional.rs b/src/args/arg_builder/positional.rs index 1e901bb142e..b3ff352b35a 100644 --- a/src/args/arg_builder/positional.rs +++ b/src/args/arg_builder/positional.rs @@ -102,33 +102,8 @@ impl<'n, 'e> PosBuilder<'n, 'e> { pb } - pub fn write_help(&self, w: &mut W, tab: &str, longest: usize, skip_pv: bool) -> io::Result<()> { - try!(write!(w, "{}", tab)); - try!(write!(w, "{}", self.name)); - if self.settings.is_set(ArgSettings::Multiple) { - try!(write!(w, "...")); - } - write_spaces!((longest + 4) - (self.to_string().len()), w); - if let Some(h) = self.help { - if h.contains("{n}") { - let mut hel = h.split("{n}"); - while let Some(part) = hel.next() { - try!(write!(w, "{}\n", part)); - write_spaces!(longest + 6, w); - try!(write!(w, "{}", hel.next().unwrap_or(""))); - } - } else { - try!(write!(w, "{}", h)); - } - if let Some(ref pv) = self.default_val { - try!(write!(w, " [default: {}]", pv)); - } - if !skip_pv { - if let Some(ref pv) = self.possible_vals { - try!(write!(w, " [values: {}]", pv.join(", "))); - } - } - } + pub fn write_help(&self, w: &mut W, tab: &str, longest: usize, skip_pv: bool, nlh: bool) -> io::Result<()> { + write_arg_help!(@pos self, w, tab, longest, skip_pv, nlh); write!(w, "\n") } } diff --git a/src/args/settings.rs b/src/args/settings.rs index 76c58850897..b5f95eb058f 100644 --- a/src/args/settings.rs +++ b/src/args/settings.rs @@ -3,13 +3,14 @@ use std::ascii::AsciiExt; bitflags! { flags Flags: u8 { - const REQUIRED = 0b0000001, - const MULTIPLE = 0b0000010, - const EMPTY_VALS = 0b0000100, - const GLOBAL = 0b0001000, - const HIDDEN = 0b0010000, - const TAKES_VAL = 0b0100000, - const USE_DELIM = 0b1000000, + const REQUIRED = 0b00000001, + const MULTIPLE = 0b00000010, + const EMPTY_VALS = 0b00000100, + const GLOBAL = 0b00001000, + const HIDDEN = 0b00010000, + const TAKES_VAL = 0b00100000, + const USE_DELIM = 0b01000000, + const NEXT_LINE_HELP = 0b10000000, } } @@ -29,7 +30,8 @@ impl ArgFlags { Global => GLOBAL, Hidden => HIDDEN, TakesValue => TAKES_VAL, - UseValueDelimiter => USE_DELIM + UseValueDelimiter => USE_DELIM, + NextLineHelp => NEXT_LINE_HELP } } @@ -57,6 +59,8 @@ pub enum ArgSettings { TakesValue, /// Determines if the argument allows values to be grouped via a delimter UseValueDelimiter, + /// Prints the help text on the line after the argument + NextLineHelp, } impl FromStr for ArgSettings { @@ -70,6 +74,7 @@ impl FromStr for ArgSettings { "hidden" => Ok(ArgSettings::Hidden), "takesvalue" => Ok(ArgSettings::TakesValue), "usevaluedelimiter" => Ok(ArgSettings::UseValueDelimiter), + "nextlinehelp" => Ok(ArgSettings::NextLineHelp), _ => Err("unknown ArgSetting, cannot convert from str".to_owned()), } } From 1e79cccc12937bc0e7cd2aad8e404410798e9fff Mon Sep 17 00:00:00 2001 From: Kevin K Date: Mon, 15 Feb 2016 01:28:29 -0500 Subject: [PATCH 2/8] docs(Arg Examples): adds better examples --- src/args/arg.rs | 847 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 733 insertions(+), 114 deletions(-) diff --git a/src/args/arg.rs b/src/args/arg.rs index c8385464615..6799cccf6c1 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -347,12 +347,29 @@ impl<'a, 'b> Arg<'a, 'b> { /// /// # Examples /// + /// To set `short` use a single valid UTF-8 codepoint. If you supply a leading `-` such as `-c` + /// it will be stripped. + /// /// ```rust /// # use clap::{App, Arg}; /// Arg::with_name("config") /// .short("c") /// # ; /// ``` + /// + /// Setting `short` allows using the argument via a single hyphen (`-`) such as `-c` + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("shorttest") + /// .arg(Arg::with_name("config") + /// .short("c")) + /// .get_matches_from(vec![ + /// "shorttest", "-c" + /// ]); + /// + /// assert!(m.is_present("config")); + /// ``` pub fn short>(mut self, s: S) -> Self { self.short = s.as_ref().trim_left_matches(|c| c == '-').chars().nth(0); self @@ -369,12 +386,30 @@ impl<'a, 'b> Arg<'a, 'b> { /// /// # Examples /// + /// To set `long` use a word containing valid UTF-8 codepoints. If you supply a dobule leading + /// `--` such as `--config` they will be stripped. Hyphens in the middle of the word, however, + /// will *not* be stripped (i.e. `config-file` is allowed) + /// /// ```rust /// # use clap::{App, Arg}; /// Arg::with_name("cfg") /// .long("config") /// # ; /// ``` + /// + /// Setting `long` allows using the argument via a double hyphen (`--`) such as `--config` + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("longtest") + /// .arg(Arg::with_name("cfg") + /// .long("config")) + /// .get_matches_from(vec![ + /// "shorttest", "--config" + /// ]); + /// + /// assert!(m.is_present("cfg")); + /// ``` pub fn long(mut self, l: &'b str) -> Self { self.long = Some(l.trim_left_matches(|c| c == '-')); self @@ -385,12 +420,46 @@ impl<'a, 'b> Arg<'a, 'b> { /// /// # Examples /// + /// Any valid `String` slice is allowed as help (i.e. only valid UTF-8). The one exception is + /// one wishes to include a newline in the help text. To include a newline **and** be properly + /// aligned with all other arguments help text, it must be specified via `{n}` instead of `\n`. + /// /// ```rust /// # use clap::{App, Arg}; /// Arg::with_name("config") /// .help("The config file used by the myprog") /// # ; /// ``` + /// + /// Setting `help` displays a short message to the side of the argument when the user passes + /// `-h` or `--help` (by default). + /// + /// ```ignore + /// # use clap::{App, Arg}; + /// let m = App::new("helptest") + /// .arg(Arg::with_name("cfg") + /// .long("config") + /// .help("Some help text describing the --config arg")) + /// .get_matches_from(vec![ + /// "shorttest", "--help" + /// ]); + /// + /// // ... + /// ``` + /// + /// The above example displays + /// + /// ```notrust + /// helptest + /// + /// USAGE: + /// helptest [FLAGS] + /// + /// FLAGS: + /// --config Some help text describing the --config arg + /// -h, --help Prints help information + /// -V, --version Prints version information + /// ``` pub fn help(mut self, h: &'b str) -> Self { self.help = Some(h); self @@ -398,11 +467,11 @@ impl<'a, 'b> Arg<'a, 'b> { /// Sets whether or not the argument is required by default. Required by default means it is /// required, when no other conflicting rules have been evaluated. Conflicting rules take - /// precedence over being required. + /// precedence over being required. **Default:** `false` /// /// **NOTE:** Flags (i.e. not positional, or arguments that take values) cannot be required. /// - /// #Example + /// # Examples /// /// ```rust /// # use clap::Arg; @@ -410,6 +479,39 @@ impl<'a, 'b> Arg<'a, 'b> { /// .required(true) /// # ; /// ``` + /// + /// Setting `required(true)` requires that the argument be used at runtime. + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let res = App::new("longtest") + /// .arg(Arg::with_name("cfg") + /// .required(true) + /// .takes_value(true) + /// .long("config")) + /// .get_matches_from_safe(vec![ + /// "shorttest", "--config", "file.conf" + /// ]); + /// + /// assert!(res.is_ok()); + /// ``` + /// + /// Setting `required(true)` and *not* supplying that argument is an error. + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind}; + /// let res = App::new("longtest") + /// .arg(Arg::with_name("cfg") + /// .required(true) + /// .takes_value(true) + /// .long("config")) + /// .get_matches_from_safe(vec![ + /// "shorttest" + /// ]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// ``` pub fn required(self, r: bool) -> Self { if r { self.set(ArgSettings::Required) } else { self.unset(ArgSettings::Required) } } @@ -420,16 +522,37 @@ impl<'a, 'b> Arg<'a, 'b> { /// **NOTE:** Conflicting rules take precedence over being required by default. Conflict rules /// only need to be set for one of the two arguments, they do not need to be set for each. /// + /// **NOTE:** Defining a conflict is two-way, but does *not* need to defined for both arguments + /// (i.e. if A conflicts with B, defining A.conflicts_with(B) is sufficient. You do not need + /// need to also do B.conflicts_with(A)) + /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// Arg::with_name("debug"); - /// // ... + /// # use clap::Arg; /// Arg::with_name("config") /// .conflicts_with("debug") /// # ; /// ``` + /// + /// Setting conflicting argument, and having both arguments present at runtime is an error. + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind}; + /// let res = App::new("conflictions") + /// .arg(Arg::with_name("cfg") + /// .takes_value(true) + /// .conflicts_with("debug") + /// .long("config")) + /// .arg(Arg::with_name("debug") + /// .long("debug")) + /// .get_matches_from_safe(vec![ + /// "conflictions", "--debug", "--config", "file.conf" + /// ]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::ArgumentConflict); + /// ``` pub fn conflicts_with(mut self, name: &'a str) -> Self { if let Some(ref mut vec) = self.blacklist { vec.push(name); @@ -439,23 +562,46 @@ impl<'a, 'b> Arg<'a, 'b> { self } - /// Sets multiple conflicting arguments by names. I.e. when using this argument, - /// the following arguments can't be present. + /// The same as `Arg::conflicts_with` but allows specifying multiple two-way conlicts per + /// argument. /// /// **NOTE:** Conflicting rules take precedence over being required by default. Conflict rules /// only need to be set for one of the two arguments, they do not need to be set for each. /// + /// **NOTE:** Defining a conflict is two-way, but does *not* need to defined for both arguments + /// (i.e. if A conflicts with B, defining A.conflicts_with(B) is sufficient. You do not need + /// need to also do B.conflicts_with(A)) + /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// Arg::with_name("debug"); - /// Arg::with_name("input"); - /// // ... + /// # use clap::Arg; /// Arg::with_name("config") /// .conflicts_with_all(&["debug", "input"]) /// # ; /// ``` + /// + /// Setting conflicting argument, and having any of the arguments present at runtime with a + /// conflicting argument is an error. + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind}; + /// let res = App::new("conflictions") + /// .arg(Arg::with_name("cfg") + /// .takes_value(true) + /// .conflicts_with_all(&["debug", "input"]) + /// .long("config")) + /// .arg(Arg::with_name("debug") + /// .long("debug")) + /// .arg(Arg::with_name("input") + /// .index(1)) + /// .get_matches_from_safe(vec![ + /// "conflictions", "--config", "file.conf", "file.txt" + /// ]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::ArgumentConflict); + /// ``` pub fn conflicts_with_all(mut self, names: &[&'a str]) -> Self { if let Some(ref mut vec) = self.blacklist { for s in names { @@ -478,18 +624,18 @@ impl<'a, 'b> Arg<'a, 'b> { /// /// ```rust /// # use clap::{App, Arg}; - /// let res = App::new("posix") + /// let m = App::new("posix") /// .arg(Arg::from_usage("-f, --flag 'some flag'") /// .conflicts_with("debug")) /// .arg(Arg::from_usage("-d, --debug 'other flag'")) /// .arg(Arg::from_usage("-c, --color 'third flag'") /// .overrides_with("flag")) - /// .get_matches_from_safe(vec!["", "-f", "-d", "-c"]); - /// // ^~~~~~~~~~~~^~~~~ flag is overriden by --color - /// assert!(res.is_ok()); - /// let m = res.unwrap(); + /// .get_matches_from(vec!["posix", "-f", "-d", "-c"]); + /// // ^~~~~~~~~~~~^~~~~ flag is overriden by color + /// /// assert!(m.is_present("color")); - /// assert!(m.is_present("debug")); + /// assert!(m.is_present("debug")); // even though flag conflicts with debug, it's as if flag + /// // was never used because it was overriden with color /// assert!(!m.is_present("flag")); /// ``` pub fn overrides_with(mut self, name: &'a str) -> Self { @@ -501,9 +647,9 @@ impl<'a, 'b> Arg<'a, 'b> { self } - /// Sets a mutually overridable argument by name. I.e. this argument and the following argument - /// will override each other in POSIX style (whichever argument was specified at runtime - /// **last** "wins") + /// Sets multiple mutually overridable arguments by name. I.e. this argument and the following + /// argument will override each other in POSIX style (whichever argument was specified at + /// runtime **last** "wins") /// /// **NOTE:** When an argument is overriden it is essentially as if it never was used, any /// conflicts, requirements, etc. are evaluated **after** all "overrides" have been removed @@ -512,17 +658,18 @@ impl<'a, 'b> Arg<'a, 'b> { /// /// ```rust /// # use clap::{App, Arg}; - /// let res = App::new("posix") + /// let m = App::new("posix") /// .arg(Arg::from_usage("-f, --flag 'some flag'") - /// .conflicts_with("debug")) + /// .conflicts_with("color")) /// .arg(Arg::from_usage("-d, --debug 'other flag'")) /// .arg(Arg::from_usage("-c, --color 'third flag'") /// .overrides_with_all(&["flag", "debug"])) - /// .get_matches_from_safe(vec!["posix", "-f", "-d", "-c"]); - /// // ^~~~~~^~~~~~^~~~~ flag and debug are overriden by --color - /// assert!(res.is_ok()); - /// let m = res.unwrap(); - /// assert!(m.is_present("color")); + /// .get_matches_from(vec!["posix", "-f", "-d", "-c"]); + /// // ^~~~~~^~~~~~~~~ flag and debug are overriden by color + /// + /// assert!(m.is_present("color")); // even though flag conflicts with color, it's as if flag + /// // and debug were never used because they were overriden + /// // with color /// assert!(!m.is_present("debug")); /// assert!(!m.is_present("flag")); /// ``` @@ -545,19 +692,48 @@ impl<'a, 'b> Arg<'a, 'b> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg, ArgGroup}; - /// let m = App::new("group_required") - /// .arg(Arg::from_usage("-f, --flag 'some flag'")) - /// .group(ArgGroup::with_name("gr") - /// .required(true) - /// .arg("some") - /// .arg("other")) - /// .arg(Arg::from_usage("--some 'some arg'")) - /// .arg(Arg::from_usage("--other 'other arg'")) - /// .get_matches_from(vec!["", "-f", "--some"]); - /// assert!(m.is_present("some")); - /// assert!(!m.is_present("other")); - /// assert!(m.is_present("flag")); + /// # use clap::Arg; + /// Arg::with_name("config") + /// .requires("input") + /// # ; + /// ``` + /// + /// Setting `requires("arg")` requires that the argument be used at runtime if the defining + /// argument is used. If the defining argument isn't used, the other arguemnt isn't required + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let res = App::new("reqtest") + /// .arg(Arg::with_name("cfg") + /// .takes_value(true) + /// .requires("input") + /// .long("config")) + /// .arg(Arg::with_name("input") + /// .index(1)) + /// .get_matches_from_safe(vec![ + /// "reqtest" + /// ]); + /// + /// assert!(res.is_ok()); // We didn't use cfg, so input wasn't required + /// ``` + /// + /// Setting `requires("arg")` and *not* supplying that argument is an error. + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind}; + /// let res = App::new("reqtest") + /// .arg(Arg::with_name("cfg") + /// .takes_value(true) + /// .requires("input") + /// .long("config")) + /// .arg(Arg::with_name("input") + /// .index(1)) + /// .get_matches_from_safe(vec![ + /// "reqtest", "--config", "file.conf" + /// ]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); /// ``` pub fn requires(mut self, name: &'a str) -> Self { if let Some(ref mut vec) = self.requires { @@ -568,7 +744,7 @@ impl<'a, 'b> Arg<'a, 'b> { self } - /// Sets arguments by names that are required when this one is present I.e. when + /// Sets multiple arguments by names that are required when this one is present I.e. when /// using this argument, the following arguments *must* be present. /// /// **NOTE:** Mutually exclusive and override rules take precedence over being required @@ -577,17 +753,54 @@ impl<'a, 'b> Arg<'a, 'b> { /// # Examples /// /// ```rust + /// # use clap::Arg; + /// Arg::with_name("config") + /// .requires_all(&["input", "output"]) + /// # ; + /// ``` + /// + /// Setting `requires_all(&["arg", "arg2"])` requires that all the arguments be used at runtime + /// if the defining argument is used. If the defining argument isn't used, the other arguemnt + /// isn't required + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let res = App::new("reqtest") + /// .arg(Arg::with_name("cfg") + /// .takes_value(true) + /// .requires("input") + /// .long("config")) + /// .arg(Arg::with_name("input") + /// .index(1)) + /// .arg(Arg::with_name("output") + /// .index(2)) + /// .get_matches_from_safe(vec![ + /// "reqtest" + /// ]); + /// + /// assert!(res.is_ok()); // We didn't use cfg, so input and output weren't required + /// ``` + /// + /// Setting `requires_all(&["arg", "arg2"])` and *not* supplying all the arguments is an error. + /// + /// ```rust /// # use clap::{App, Arg, ErrorKind}; - /// let result = App::new("flag_required") - /// .arg(Arg::from_usage("-d 'debugging mode'")) - /// .arg(Arg::from_usage("-f, --flag 'some flag'") - /// .requires_all(&["color", "d"])) - /// .arg(Arg::from_usage("-c, --color 'third flag'")) - /// .get_matches_from_safe(vec!["flag_required", "-f"]); - /// assert!(result.is_err()); - /// let err = result.err().unwrap(); - /// assert_eq!(err.kind, ErrorKind::MissingRequiredArgument); - /// # + /// let res = App::new("reqtest") + /// .arg(Arg::with_name("cfg") + /// .takes_value(true) + /// .requires_all(&["input", "output"]) + /// .long("config")) + /// .arg(Arg::with_name("input") + /// .index(1)) + /// .arg(Arg::with_name("output") + /// .index(2)) + /// .get_matches_from_safe(vec![ + /// "reqtest", "--config", "file.conf", "in.txt" + /// ]); + /// + /// assert!(res.is_err()); + /// // We didn't use output + /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); /// ``` pub fn requires_all(mut self, names: &[&'a str]) -> Self { if let Some(ref mut vec) = self.requires { @@ -600,7 +813,18 @@ impl<'a, 'b> Arg<'a, 'b> { self } - /// Specifies that the argument takes an additional value at run time. + /// Specifies that the argument takes a value at run time. + /// + /// **NOTE:** values for arguments may be specified in any of the following methods + /// + /// * Using a space such as `-o value` or `--option value` + /// * Using an equals and no space such as `-o=value` or `--option=value` + /// * Use a short and no space such as `-ovalue` + /// + /// **NOTE:** By default, values are delimted by commas, meaning `--option=val1,val2,val3` is + /// is three values for the `--option` argument. If you wish to change the delimiter to another + /// character you can use `Arg::value_delimiter(char)`, alternatively you can delimiting values + /// **OFF** by using `Arg::use_delimiter(false)` /// /// # Examples /// @@ -610,36 +834,89 @@ impl<'a, 'b> Arg<'a, 'b> { /// .takes_value(true) /// # ; /// ``` + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("posvals") + /// .arg(Arg::with_name("mode") + /// .long("mode") + /// .takes_value(true)) + /// .get_matches_from(vec!["posvals", "--mode", "fast"]); + /// + /// assert!(m.is_present("mode")); + /// assert_eq!(m.value_of("mode"), Some("fast")); + /// ``` pub fn takes_value(self, tv: bool) -> Self { if tv { self.set(ArgSettings::TakesValue) } else { self.unset(ArgSettings::TakesValue) } } /// Specifies the index of a positional argument **starting at** 1. /// + /// **NOTE:** The index refers to position according to **other positional argument**. It does + /// not define position in the argument list as a whole. + /// + /// **NOTE:** If no `short`, or `long` have been defined, you can optionally leave off the + /// `index` method, and the index will be assigned in order of evaluation. Utilizing the + /// `index` method allows for setting indexes out of order + /// + /// **NOTE:** When utilized with `multiple(true)`, only the **last** psoitional argument may + /// be defined as multiple (i.e. with the highest index) + /// + /// # Panics + /// + /// Although not in this method directly, `App` will `panic!` if indexes are skipped (such as + /// defining `index(1)` and `index(3)` but not `index(2)`, or a positional argument is defined + /// as multiple and is not the highest index + /// /// # Examples /// /// ```rust /// # use clap::{App, Arg}; - /// Arg::with_name("file") - /// .index(1) + /// Arg::with_name("config") + /// .index(1) /// # ; /// ``` + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("posvals") + /// .arg(Arg::with_name("mode") + /// .index(1)) + /// .arg(Arg::with_name("debug") + /// .long("debug")) + /// .get_matches_from(vec!["posvals", "--debug", "fast"]); + /// + /// assert!(m.is_present("mode")); + /// assert_eq!(m.value_of("mode"), Some("fast")); // notice index(1) means "first positional" + /// // *not* first argument + /// ``` pub fn index(mut self, idx: u64) -> Self { self.index = Some(idx); self } - /// Specifies that the flag or option may appear more than once. For flags, this results - /// in the number of occurrences of the flag being recorded. For example `-ddd` would count as - /// three occurrences. The form `-d -d -d` would also be recognized as three occurrences. For - /// options there is a distinct difference in multiple occurrences vs multiple values. + /// Specifies that the argument may appear more than once. For flags, this results + /// in the number of occurrences of the flag being recorded. For example `-ddd` or `-d -d -d` + /// would count as three occurrences. For options there is a distinct difference in multiple + /// occurrences vs multiple values. /// - /// For example, `--opt val1 val2` is one occurrence, but multiple values. `--opt val1 --opt - /// val2` is multiple occurrences. This setting applies to occurrences and **not** values. + /// For example, `--opt val1 val2` is one occurrence, but two values. Whereas + /// `--opt val1 --opt val2` is two occurrences. /// - /// To specify that an option may receive multiple values, use `Arg::min_values`, - /// `Arg::max_values`, or `Arg::number_of_values` depending on your use case. Note also, that - /// `Arg::value_names` implicitly sets multiple values, but not multiple occurrences. + /// **WARNING:** + /// + /// Setting `multipe(true)` for an option allows multiple values **and** multiple occurrences + /// because it isn't possible to more occurrences than values for options. Because multiple + /// values are allowed, `--option val1 val2 val3` is perfectly valid, be careful when designing + /// a CLI where positional arguments are expectd after a option which accepts multiple values, + /// as `clap` will continue parsing *values* until it reaches the max or specific number of values defined, or another flag + /// or option. + /// + /// **Pro Tip**: + /// + /// It's possible to define an option which allows multiple occurrences, but only one value per + /// occurrence. To do this use `Arg::number_of_values(1)` in coordination with + /// `Arg::multiple(true)`. /// /// # Examples /// @@ -650,6 +927,116 @@ impl<'a, 'b> Arg<'a, 'b> { /// .multiple(true) /// # ; /// ``` + /// An example with flags + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("mults") + /// .arg(Arg::with_name("verbose") + /// .multiple(true) + /// .short("v")) + /// .get_matches_from(vec!["mults", "-v", "-v", "-v"]); // note, -vvv would have same result + /// + /// assert!(m.is_present("verbose")); + /// assert_eq!(m.occurrences_of("verbose"), 3); + /// ``` + /// + /// An example with options + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("mults") + /// .arg(Arg::with_name("file") + /// .multiple(true) + /// .takes_value(true) + /// .short("F")) + /// .get_matches_from(vec!["mults", "-F", "file1", "file2", "file3"]); + /// + /// assert!(m.is_present("file")); + /// assert_eq!(m.occurrences_of("file"), 1); // notice only one occurrence + /// let files: Vec<_> = m.values_of("file").unwrap().collect(); + /// assert_eq!(files, ["file1", "file2", "file3"]); + /// ``` + /// This is functionally equivilant to the example above + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("mults") + /// .arg(Arg::with_name("file") + /// .multiple(true) + /// .takes_value(true) + /// .short("F")) + /// .get_matches_from(vec!["mults", "-F", "file1", "-F", "file2", "-F", "file3"]); + /// let files: Vec<_> = m.values_of("file").unwrap().collect(); + /// assert_eq!(files, ["file1", "file2", "file3"]); + /// + /// assert!(m.is_present("file")); + /// assert_eq!(m.occurrences_of("file"), 3); // Notice 3 occurrences + /// let files: Vec<_> = m.values_of("file").unwrap().collect(); + /// assert_eq!(files, ["file1", "file2", "file3"]); + /// ``` + /// + /// A common mistake is to define an option which allows multiples, and a positional argument + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("mults") + /// .arg(Arg::with_name("file") + /// .multiple(true) + /// .takes_value(true) + /// .short("F")) + /// .arg(Arg::with_name("word") + /// .index(1)) + /// .get_matches_from(vec!["mults", "-F", "file1", "file2", "file3", "word"]); + /// + /// assert!(m.is_present("file")); + /// let files: Vec<_> = m.values_of("file").unwrap().collect(); + /// assert_eq!(files, ["file1", "file2", "file3", "word"]); // wait...what?! + /// assert!(!m.is_present("word")); // but we clearly used word! + /// ``` + /// The problem is clap doesn't know when to stop parsing values for "files". This is further + /// compounded by if we'd said `word -F file1 file2` it would have worked fine, so it would + /// appear to only fail sometimes...not good! + /// + /// A solution for the example above is to specify that `-F` only accepts one value, but is + /// allowed to appear multiple times + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("mults") + /// .arg(Arg::with_name("file") + /// .multiple(true) + /// .takes_value(true) + /// .number_of_values(1) + /// .short("F")) + /// .arg(Arg::with_name("word") + /// .index(1)) + /// .get_matches_from(vec!["mults", "-F", "file1", "-F", "file2", "-F", "file3", "word"]); + /// + /// assert!(m.is_present("file")); + /// let files: Vec<_> = m.values_of("file").unwrap().collect(); + /// assert_eq!(files, ["file1", "file2", "file3"]); + /// assert!(m.is_present("word")); + /// assert_eq!(m.value_of("word"), Some("word")); + /// ``` + /// As a final example, notice if we define `number_of_values(1)` and try to run the problem + /// example above, it would have been a runtime error with a pretty message to the user :) + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind}; + /// let res = App::new("mults") + /// .arg(Arg::with_name("file") + /// .multiple(true) + /// .takes_value(true) + /// .number_of_values(1) + /// .short("F")) + /// .arg(Arg::with_name("word") + /// .index(1)) + /// .get_matches_from_safe(vec!["mults", "-F", "file1", "file2", "file3", "word"]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); + /// ``` pub fn multiple(self, multi: bool) -> Self { if multi { self.set(ArgSettings::Multiple) } else { self.unset(ArgSettings::Multiple) } } @@ -674,14 +1061,34 @@ impl<'a, 'b> Arg<'a, 'b> { /// .global(true) /// # ; /// ``` + /// + /// For example, assume an appliction with two subcommands, and you'd like to define a + /// `--verbose` flag that can be called on any of the subcommands and parent, but you don't + /// want to clutter the source with three duplicate `Arg` definitions. + /// + /// ```rust + /// # use clap::{App, Arg, SubCommand}; + /// let m = App::new("mults") + /// .arg(Arg::with_name("verb") + /// .long("verbose") + /// .short("v") + /// .global(true)) + /// .subcommand(SubCommand::with_name("test")) + /// .subcommand(SubCommand::with_name("do-stuff")) + /// .get_matches_from(vec!["mults", "do-stuff", "--verbose"]); + /// + /// assert_eq!(m.subcommand_name(), Some("do-stuff")); + /// let sub_m = m.subcommand_matches("do-stuff").unwrap(); + /// assert!(sub_m.is_present("verb")); + /// ``` pub fn global(self, g: bool) -> Self { if g { self.set(ArgSettings::Global) } else { self.unset(ArgSettings::Global) } } - /// Allows an argument to accept explicit empty values. An empty value must be specified at the - /// command line with an explicit `""`, or `''` + /// Allows an argument to accept explicitly empty values. An empty value must be specified at + /// the command line with an explicit `""`, or `''` /// - /// **NOTE:** Defaults to `true` (Explicit empty values are allowed) + /// **NOTE:** Defaults to `true` (Explicitly empty values are allowed) /// /// **NOTE:** Implicitly sets `takes_value(true)` when set to `false` /// @@ -694,6 +1101,21 @@ impl<'a, 'b> Arg<'a, 'b> { /// .empty_values(false) /// # ; /// ``` + /// The default is to allow empty values, such as `--option ""` would be an empty value. But + /// we can change to make empty values become an error. + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind}; + /// let res = App::new("evals") + /// .arg(Arg::with_name("cfg") + /// .long("config") + /// .short("v") + /// .empty_values(false)) + /// .get_matches_from_safe(vec!["evals", "--config="]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue); + /// ``` pub fn empty_values(mut self, ev: bool) -> Self { if ev { self.set(ArgSettings::EmptyValues) @@ -715,6 +1137,34 @@ impl<'a, 'b> Arg<'a, 'b> { /// .hidden(true) /// # ; /// ``` + /// Setting `hidden(true)` will hide the argument when displaying help text + /// + /// ```ignore + /// # use clap::{App, Arg}; + /// let m = App::new("helptest") + /// .arg(Arg::with_name("cfg") + /// .long("config") + /// .hidden(true) + /// .help("Some help text describing the --config arg")) + /// .get_matches_from(vec![ + /// "shorttest", "--help" + /// ]); + /// + /// // ... + /// ``` + /// + /// The above example displays + /// + /// ```notrust + /// helptest + /// + /// USAGE: + /// helptest [FLAGS] + /// + /// FLAGS: + /// -h, --help Prints help information + /// -V, --version Prints version information + /// ``` pub fn hidden(self, h: bool) -> Self { if h { self.set(ArgSettings::Hidden) } else { self.unset(ArgSettings::Hidden) } } @@ -728,33 +1178,37 @@ impl<'a, 'b> Arg<'a, 'b> { /// /// ```rust /// # use clap::{App, Arg}; - /// let res = App::new("possible_values") - /// .arg(Arg::with_name("option") - /// .short("-o") - /// .long("--option") + /// Arg::with_name("mode") + /// .takes_value(true) + /// .possible_values(&["fast", "slow", "medium"]) + /// # ; + /// ``` + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("posvals") + /// .arg(Arg::with_name("mode") + /// .long("mode") /// .takes_value(true) - /// .possible_values(&["fast", "slow"])) - /// .get_matches_from_safe(vec!["myprog", "--option", "fast"]); - /// assert!(res.is_ok()); - /// let m = res.unwrap(); - /// assert!(m.is_present("option")); - /// assert_eq!(m.value_of("option"), Some("fast")); + /// .possible_values(&["fast", "slow", "medium"])) + /// .get_matches_from(vec!["posvals", "--mode", "fast"]); + /// assert!(m.is_present("mode")); + /// assert_eq!(m.value_of("mode"), Some("fast")); /// ``` /// - /// The next example shows a failed parse + /// The next example shows a failed parse from using a value which wasn't defined as one of the + /// possible values. /// /// ```rust /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("possible_values") - /// .arg(Arg::with_name("option") - /// .short("-o") - /// .long("--option") + /// let res = App::new("posvals") + /// .arg(Arg::with_name("mode") + /// .long("mode") /// .takes_value(true) - /// .possible_values(&["fast", "slow"])) - /// .get_matches_from_safe(vec!["myprog", "--option", "wrong"]); + /// .possible_values(&["fast", "slow", "medium"])) + /// .get_matches_from_safe(vec!["myprog", "--mode", "wrong"]); /// assert!(res.is_err()); - /// let err = res.unwrap_err(); - /// assert_eq!(err.kind, ErrorKind::InvalidValue); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::InvalidValue); /// ``` pub fn possible_values(mut self, names: &[&'b str]) -> Self { if let Some(ref mut vec) = self.possible_vals { @@ -767,42 +1221,50 @@ impl<'a, 'b> Arg<'a, 'b> { self } - /// Specifies a possible value for this argument. At runtime, `clap` verifies that only - /// one of the specified values was used, or fails with error message. + /// Specifies a possible value for this argument, one at a time. At runtime, `clap` verifies + /// that only one of the specified values was used, or fails with error message. /// /// # Examples /// /// ```rust /// # use clap::{App, Arg}; - /// let res = App::new("possible_values") - /// .arg(Arg::with_name("option") - /// .short("-o") - /// .long("--option") + /// Arg::with_name("mode") + /// .takes_value(true) + /// .possible_value("fast") + /// .possible_value("slow") + /// .possible_value("medium") + /// # ; + /// ``` + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("posvals") + /// .arg(Arg::with_name("mode") + /// .long("mode") /// .takes_value(true) + /// .possible_value("fast") /// .possible_value("slow") - /// .possible_value("fast")) - /// .get_matches_from_safe(vec!["myprog", "--option", "fast"]); - /// assert!(res.is_ok()); - /// let m = res.unwrap(); - /// assert!(m.is_present("option")); - /// assert_eq!(m.value_of("option"), Some("fast")); + /// .possible_value("medium")) + /// .get_matches_from(vec!["posvals", "--mode", "fast"]); + /// assert!(m.is_present("mode")); + /// assert_eq!(m.value_of("mode"), Some("fast")); /// ``` /// - /// The next example shows a failed parse + /// The next example shows a failed parse from using a value which wasn't defined as one of the + /// possible values. /// /// ```rust /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("possible_values") - /// .arg(Arg::with_name("option") - /// .short("-o") - /// .long("--option") + /// let res = App::new("posvals") + /// .arg(Arg::with_name("mode") + /// .long("mode") /// .takes_value(true) + /// .possible_value("fast") /// .possible_value("slow") - /// .possible_value("fast")) - /// .get_matches_from_safe(vec!["myprog", "--option", "wrong"]); + /// .possible_value("medium")) + /// .get_matches_from_safe(vec!["myprog", "--mode", "wrong"]); /// assert!(res.is_err()); - /// let err = res.unwrap_err(); - /// assert_eq!(err.kind, ErrorKind::InvalidValue); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::InvalidValue); /// ``` pub fn possible_value(mut self, name: &'b str) -> Self { if let Some(ref mut vec) = self.possible_vals { @@ -820,10 +1282,26 @@ impl<'a, 'b> Arg<'a, 'b> { /// ```rust /// # use clap::{App, Arg}; /// Arg::with_name("debug") - /// .index(1) + /// .long("debug") /// .group("mode") /// # ; /// ``` + /// + /// Multiple arguments can be a member of a single group and then the group checked as if it + /// was one of said arguments. + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("groups") + /// .arg(Arg::with_name("debug") + /// .long("debug") + /// .group("mode")) + /// .arg(Arg::with_name("verbose") + /// .long("verbose") + /// .group("mode")) + /// .get_matches_from(vec!["posvals", "--debug"]); + /// assert!(m.is_present("mode")); + /// ``` pub fn group(mut self, name: &'a str) -> Self { self.group = Some(name); self @@ -847,6 +1325,21 @@ impl<'a, 'b> Arg<'a, 'b> { /// .number_of_values(3) /// # ; /// ``` + /// + /// Not supplying the correct number of values is an error + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind}; + /// let res = App::new("numvals") + /// .arg(Arg::with_name("file") + /// .takes_value(true) + /// .number_of_values(2) + /// .short("F")) + /// .get_matches_from_safe(vec!["mults", "-F", "file1"]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::WrongNumberOfValues); + /// ``` pub fn number_of_values(mut self, qty: u64) -> Self { self.num_vals = Some(qty); self @@ -908,6 +1401,38 @@ impl<'a, 'b> Arg<'a, 'b> { /// .max_values(3) /// # ; /// ``` + /// + /// Supplying less than the maximum number of values is allowed + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let res = App::new("numvals") + /// .arg(Arg::with_name("file") + /// .takes_value(true) + /// .max_values(3) + /// .short("F")) + /// .get_matches_from_safe(vec!["mults", "-F", "file1", "file2"]); + /// + /// assert!(res.is_ok()); + /// let m = res.unwrap(); + /// let files: Vec<_> = m.values_of("file").unwrap().collect(); + /// assert_eq!(files, ["file1", "file2"]); + /// ``` + /// + /// Supplying more than the maximum number of values is an error + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind}; + /// let res = App::new("numvals") + /// .arg(Arg::with_name("file") + /// .takes_value(true) + /// .max_values(2) + /// .short("F")) + /// .get_matches_from_safe(vec!["mults", "-F", "file1", "file2", "file3"]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::TooManyValues); + /// ``` pub fn max_values(mut self, qty: u64) -> Self { self.max_vals = Some(qty); self @@ -932,6 +1457,38 @@ impl<'a, 'b> Arg<'a, 'b> { /// .min_values(3) /// # ; /// ``` + /// + /// Supplying more than the minimum number of values is allowed + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let res = App::new("numvals") + /// .arg(Arg::with_name("file") + /// .takes_value(true) + /// .min_values(2) + /// .short("F")) + /// .get_matches_from_safe(vec!["mults", "-F", "file1", "file2", "file3"]); + /// + /// assert!(res.is_ok()); + /// let m = res.unwrap(); + /// let files: Vec<_> = m.values_of("file").unwrap().collect(); + /// assert_eq!(files, ["file1", "file2", "file3"]); + /// ``` + /// + /// Supplying less than the mainimum number of values is an error + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind}; + /// let res = App::new("numvals") + /// .arg(Arg::with_name("file") + /// .takes_value(true) + /// .min_values(2) + /// .short("F")) + /// .get_matches_from_safe(vec!["mults", "-F", "file1"]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::TooFewValues); + /// ``` pub fn min_values(mut self, qty: u64) -> Self { self.min_vals = Some(qty); self.set(ArgSettings::TakesValue) @@ -1023,10 +1580,17 @@ impl<'a, 'b> Arg<'a, 'b> { self } - /// Specifies names for values of option arguments. These names are cosmetic only, used for - /// help and usage strings only. The names are **not** used to access arguments. The values of - /// the arguments are accessed in numeric order (i.e. if you specify two names `one` and `two` - /// `one` will be the first matched value, `two` will be the second). + /// Specify multiple names for values of option arguments. These names are cosmetic only, used + /// for help and usage strings only. The names are **not** used to access arguments. The values + /// of the arguments are accessed in numeric order (i.e. if you specify two names `one` and + /// `two` `one` will be the first matched value, `two` will be the second). + /// + /// This setting can be very helpful when describing the type of input the user should be + /// using, such as `FILE`, `INTERFACE`, etc. Although not required, it's somewhat convention to + /// use all capital letters for the value name. + /// + /// **Pro Tip:** It may help to use `Arg::next_line_help(true)` if there are long, or multiple + /// value names in order to not throw off the help text alignment of all options. /// /// **NOTE:** This implicitly sets `.number_of_values()` if the number of value names is /// greater than one. I.e. be aware that the number of "names" you set for the values, will be @@ -1045,6 +1609,32 @@ impl<'a, 'b> Arg<'a, 'b> { /// .value_names(&["fast", "slow"]) /// # ; /// ``` + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let app = App::new("valnames") + /// .arg(Arg::with_name("io") + /// .long("io-files") + /// .value_names(&["INFILE", "OUTFILE"])) + /// .get_matches_from(vec![ + /// "valnames", "--help" + /// ]); + /// ``` + /// Running the above program produces the following output + /// + /// ```notrust + /// valnames + /// + /// USAGE: + /// valnames [FLAGS] [OPTIONS] + /// + /// FLAGS: + /// -h, --help Prints help information + /// -V, --version Prints version information + /// + /// OPTIONS: + /// --io-files Some help text + /// ``` pub fn value_names(mut self, names: &[&'b str]) -> Self { self.setb(ArgSettings::TakesValue); if let Some(ref mut vals) = self.val_names { @@ -1064,7 +1654,10 @@ impl<'a, 'b> Arg<'a, 'b> { } /// Specifies the name for value of option or positional arguments inside of help documenation. - /// This name is cosmetic only, the name is **not** used to access arguments. + /// This name is cosmetic only, the name is **not** used to access arguments. This setting can + /// be very helpful when describing the type of input the user should be using, such as `FILE`, + /// `INTERFACE`, etc. Although not required, it's somewhat convention to use all capital + /// letters for the value name. /// /// **NOTE:** implicitly sets `Arg::takes_value(true)` /// @@ -1072,11 +1665,37 @@ impl<'a, 'b> Arg<'a, 'b> { /// /// ```rust /// # use clap::{App, Arg}; - /// Arg::with_name("input") - /// .index(1) + /// Arg::with_name("cfg") + /// .long("config") /// .value_name("FILE") /// # ; /// ``` + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let app = App::new("valnames") + /// .arg(Arg::with_name("config") + /// .long("config") + /// .value_name("FILE")) + /// .get_matches_from(vec![ + /// "valnames", "--help" + /// ]); + /// ``` + /// Running the above program produces the following output + /// + /// ```notrust + /// valnames + /// + /// USAGE: + /// valnames [FLAGS] [OPTIONS] + /// + /// FLAGS: + /// -h, --help Prints help information + /// -V, --version Prints version information + /// + /// OPTIONS: + /// --config Some help text + /// ``` pub fn value_name(mut self, name: &'b str) -> Self { self.setb(ArgSettings::TakesValue); if let Some(ref mut vals) = self.val_names { From 6efa298cba20dc6784a905f783e1954a615b367d Mon Sep 17 00:00:00 2001 From: Kevin K Date: Wed, 17 Feb 2016 23:47:03 -0500 Subject: [PATCH 3/8] test: fixes failing doc tests --- clap-tests/run_tests.py | 8 ++++---- tests/app_settings.rs | 4 ++-- tests/help.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clap-tests/run_tests.py b/clap-tests/run_tests.py index 1fc34adda45..952bb977829 100755 --- a/clap-tests/run_tests.py +++ b/clap-tests/run_tests.py @@ -31,9 +31,9 @@ -o, --option ... tests options ARGS: - positional tests positionals - positional2 tests positionals with exclusions - positional3... tests positionals with specific values [values: vi, emacs] + tests positionals + tests positionals with exclusions + ... tests positionals with specific values [values: vi, emacs] SUBCOMMANDS: help Prints this message @@ -152,7 +152,7 @@ -o, --option ... tests options ARGS: - scpositional tests positionals''' + tests positionals''' _scfop = '''flag NOT present option NOT present diff --git a/tests/app_settings.rs b/tests/app_settings.rs index 6ffbb7559cf..d25a9abe137 100644 --- a/tests/app_settings.rs +++ b/tests/app_settings.rs @@ -102,7 +102,7 @@ OPTIONS: -V, --version Prints version information ARGS: - arg1 some pos arg\n")); + some pos arg\n")); } #[test] @@ -135,7 +135,7 @@ OPTIONS: -o, --opt some option ARGS: - arg1 some pos arg\n")); + some pos arg\n")); } #[test] diff --git a/tests/help.rs b/tests/help.rs index c1557e38ba0..676d310002f 100644 --- a/tests/help.rs +++ b/tests/help.rs @@ -112,5 +112,5 @@ OPTIONS: -o, --opt some option [values: one, two] ARGS: - arg1 some pos arg [values: three, four]\n")); + some pos arg [values: three, four]\n")); } From e2ec5f09ebfb757aef67b148b58081050cbff075 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Thu, 18 Feb 2016 23:56:44 -0500 Subject: [PATCH 4/8] chore: clippy run --- src/app/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/parser.rs b/src/app/parser.rs index 4a9deb2d469..1cb3ca4fad8 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -1358,7 +1358,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { fn write_version(&self, w: &mut W) -> io::Result<()> { if let Some(bn) = self.meta.bin_name.as_ref() { - if bn.contains(" ") { + if bn.contains(' ') { // Incase we're dealing with subcommands i.e. git mv is translated to git-mv writeln!(w, "{} {}", bn.replace(" ", "-"), self.meta.version.unwrap_or("".into())) } else { From 60c042481fab34d75e19861f069472db8065b217 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Thu, 18 Feb 2016 23:57:39 -0500 Subject: [PATCH 5/8] chore: moves github specific files to .github dir --- CONTRIBUTING.md => .github/CONTRIBUTING.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CONTRIBUTING.md => .github/CONTRIBUTING.md (100%) diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md From 3c8db0e9be1d24edaad364359513cbb02abb4186 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Fri, 19 Feb 2016 00:08:13 -0500 Subject: [PATCH 6/8] docs(AppSettings): clarifies that AppSettings do not propagate Closes #429 --- src/app/settings.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/settings.rs b/src/app/settings.rs index 352272ac027..f88b30ac8d2 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -62,6 +62,9 @@ impl AppFlags { } /// Application level settings, which affect how `App` operates +/// +/// **NOTE:** When these settings are used, they apply only to current command, and are *not* +/// propagated down or up through child or parent subcommands #[derive(Debug, PartialEq, Copy, Clone)] pub enum AppSettings { /// Allows subcommands to override all requirements of the parent command. For example @@ -376,7 +379,8 @@ pub enum AppSettings { /// assert_eq!(m.value_of_os("arg").unwrap().as_bytes(), &[0xe9]); /// ``` AllowInvalidUtf8, - /// Specifies that leading hyphens are allowed in argument values, such as `-10` + /// Specifies that leading hyphens are allowed in argument *values*, such as negative numbers + /// `-10` /// /// **NOTE:** This can only be set application wide and not on a per argument basis. /// From a834ae699088b7b427144be7d54ea5df7a8bd3cf Mon Sep 17 00:00:00 2001 From: Kevin K Date: Fri, 19 Feb 2016 00:09:10 -0500 Subject: [PATCH 7/8] chore: hides ArgSettings in docs --- src/args/settings.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/args/settings.rs b/src/args/settings.rs index b5f95eb058f..a18e144e1b3 100644 --- a/src/args/settings.rs +++ b/src/args/settings.rs @@ -44,6 +44,7 @@ impl Default for ArgFlags { /// Various settings that apply to arguments and may be set, unset, and checked via getter/setter /// methods `Arg::set`, `Arg::unset`, and `Arg::is_set` #[derive(Debug, PartialEq, Copy, Clone)] +#[doc(hidden)] pub enum ArgSettings { /// The argument must be used Required, From d0c956c1cee45adc4cf1d97853ea2181e7cad714 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Fri, 19 Feb 2016 10:47:39 -0500 Subject: [PATCH 8/8] chore: increase version --- CHANGELOG.md | 14 +++++++++++ Cargo.toml | 2 +- README.md | 68 +++++----------------------------------------------- 3 files changed, 21 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d93d51f081..f87465ee124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ + +### v2.1.1 (2016-02-19) + + +#### Documentation + +* **AppSettings:** clarifies that AppSettings do not propagate ([3c8db0e9](https://github.com/kbknapp/clap-rs/commit/3c8db0e9be1d24edaad364359513cbb02abb4186), closes [#429](https://github.com/kbknapp/clap-rs/issues/429)) +* **Arg Examples:** adds better examples ([1e79cccc](https://github.com/kbknapp/clap-rs/commit/1e79cccc12937bc0e7cd2aad8e404410798e9fff)) + +#### Improvements + +* **Help:** adds setting for next line help by arg ([066df748](https://github.com/kbknapp/clap-rs/commit/066df7486e684cf50a8479a356a12ba972c34ce1), closes [#427](https://github.com/kbknapp/clap-rs/issues/427)) + + ## v2.1.0 (2016-02-10) diff --git a/Cargo.toml b/Cargo.toml index 6f9510d28c7..b34056081aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clap" -version = "2.1.0" +version = "2.1.1" authors = ["Kevin K. "] exclude = ["examples/*", "clap-tests/*", "tests/*", "benches/*", "*.png", "clap-perf/*"] description = "A simple to use, efficient, and full featured Command Line Argument Parser" diff --git a/README.md b/README.md index 59ee380b023..d2255c9bae4 100644 --- a/README.md +++ b/README.md @@ -38,72 +38,16 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc) ## What's New -v**2.0** has been released! This means fixing some pain points, new features, better documentation, improved ergonomics, and also some minor breaking changes if you're used to v1.x +In v2.1.1 #### New Features -Here are some key points about the 2.x release + * **Default Values**: Args can now specify default values + * **Next Line Help**: Args can have help strings on the line following the argument (useful for long arguments, or those with many values). This can be set command-wide or for individual args - * Support for arguments with invalid UTF-8 values!: Helps with POSIX and Unix like OSs - * Even better performance boost! - * Far better documentation - * Support for delimited values - * Support for custom delimiter in values - * Support for external subcommands - * Support for options that act as flags (i.e., ones which optionally have no value) - * Support for negative numbers as arguments (i.e., `-10`, etc.) - * Better Errors and error handling - * Improved "from usage" strings - * Better support for generics, instead of being locked in to `Vec` at key points - * Improved macros - * Better regression testing - * Vastly improved ergonomics - * Numerous bug fixes +#### Improvements -![clap Performance Graph](https://github.com/kbknapp/clap-rs/blob/master/clap-perf/clap_perf.png) - -#### Breaking Changes - -Below is a list of breaking changes between 1.x and 2.x and how you can change your code to update. - -* **Fewer lifetimes! Yay!** - * `App<'a, 'b, 'c, 'd, 'e, 'f>` => `App<'a, 'b>` - * `Arg<'a, 'b, 'c, 'd, 'e, 'f>` => `Arg<'a, 'b>` - * `ArgMatches<'a, 'b>` => `ArgMatches<'a>` -* **Simply Renamed** - * `App::arg_group` => `App::group` - * `App::arg_groups` => `App::groups` - * `ArgGroup::add` => `ArgGroup::arg` - * `ArgGroup::add_all` => `ArgGroup::args` - * `ClapError` => `Error` - * `ClapResult` => `Result` - * `ClapErrorType` => `ErrorKind` - * struct field `ClapError::error_type` => `Error::kind` -* **Removed Deprecated Functions and Methods** - * `App::subcommands_negate_reqs` - * `App::subcommand_required` - * `App::arg_required_else_help` - * `App::global_version(bool)` - * `App::versionless_subcommands` - * `App::unified_help_messages` - * `App::wait_on_error` - * `App::subcommand_required_else_help` - * `SubCommand::new` - * `App::error_on_no_subcommand` - * `Arg::new` - * `Arg::mutually_excludes` - * `Arg::mutually_excludes_all` - * `Arg::mutually_overrides_with` - * `simple_enum!` -* **Renamed Errors Variants** - * `InvalidUnicode` => `InvalidUtf8` - * `InvalidArgument` => `UnknownArgument` -* **Usage Parser** - * Value names can now be specified inline: `-o, --option 'some option which takes two files'` - * **There is now a priority of order to determine the name** - This is perhaps the biggest breaking change. See the documentation for full details. Prior to this change, the value name took precedence. **Ensure your args are using the proper names (i.e. typically the long or short and NOT the value name) throughout the code** -* `ArgMatches::values_of` returns an `Values` now which implements `Iterator` (should not break any code) -* `crate_version!` returns `&'static str` instead of `String` -* Using the `clap_app!` macro requires compiling with the `unstable` feature because the syntax could change slightly in the future + * **Documenation Examples**: The examples in the documentation have been vastly improved For full details, see [CHANGELOG.md](https://github.com/kbknapp/clap-rs/blob/master/CHANGELOG.md) @@ -609,6 +553,6 @@ As of 2.0.0 (From 1.x) Old method names will be left around for several minor version bumps, or one major version bump. -As of 2.0.0: +As of 2.1.1: * None!