From db0bef9a12eb5f3aa45e83b471b8e0f32d627931 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Tue, 10 May 2016 21:32:20 -0400 Subject: [PATCH 01/18] feat(Subcommands): allows using enums instead of strings for SubCommands Example: ``` #[macro_use] extern crate clap; use clap::{App, SubCommand}; // Note lowercase variants, the subcommand will be exactly as typed here subcommands!{ enum MyProg { show, delete, make } } // Alternatively, if you wish to have variants which display // differently, or contain hyphens ("-") one can use this variation of // the macro subcommands!{ enum MyProgAlt { Show => "show", Delete => "delete", DoStuff => "do-stuff" } } fn main() { let m = App::new("myprog") .subcommand(SubCommand::with_name(MyProg::show)) .subcommand(SubCommand::with_name(MyProg::delete)) .subcommand(SubCommand::with_name(MyProg::make)) .get_matches_from(vec!["myprog", "show"]); match m.subcommand() { Some((MyProg::show, _)) => println!("'myprog show' was used"), Some((MyProg::delete, _)) => println!("'myprog delete' was used"), Some((MyProg::make, _)) => println!("'myprog make' was used"), None => println!("No subcommand was used"), } } ``` This has the benefit of giving non-exhaustive compile time errors when you add subcommands but forget to check or handle them later. --- src/app/parser.rs | 123 +++++---- src/args/arg_matches.rs | 5 +- src/args/mod.rs | 1 + src/args/subcommand.rs | 28 +- src/lib.rs | 9 +- src/macros.rs | 400 +++++++++++++++++++++++++++- src/{osstringext.rs => osstrext.rs} | 76 +++--- src/vecext.rs | 60 +++++ 8 files changed, 596 insertions(+), 106 deletions(-) rename src/{osstringext.rs => osstrext.rs} (70%) create mode 100644 src/vecext.rs diff --git a/src/app/parser.rs b/src/app/parser.rs index ecdaaa855f8..9aebcb05acd 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -12,7 +12,7 @@ use app::help::Help; use app::App; use args::{Arg, ArgGroup, FlagBuilder, OptBuilder, PosBuilder}; use app::settings::{AppFlags, AppSettings}; -use args::{AnyArg, ArgMatcher}; +use args::{AnyArg, ArgMatcher, ArgMatches}; use args::settings::ArgSettings; use errors::{Error, ErrorKind}; use errors::Result as ClapResult; @@ -21,9 +21,9 @@ use suggestions; use INTERNAL_ERROR_MSG; use SubCommand; use fmt::Format; -use osstringext::OsStrExt2; use app::meta::AppMeta; use args::MatchedArg; +use vecext::VecExt; #[allow(missing_debug_implementations)] #[doc(hidden)] @@ -387,20 +387,21 @@ impl<'a, 'b> Parser<'a, 'b> // but no 2) if let Some((idx, ref p)) = self.positionals.iter().rev().next() { debug_assert!(!(idx != self.positionals.len()), - format!("Found positional argument \"{}\" who's index is {} but there are \ + format!("Found positional argument \"{}\" who's index is {} but there are \ only {} positional arguments defined", - p.name, - idx, - self.positionals.len())); + p.name, + idx, + self.positionals.len())); } // Next we verify that only the highest index has a .multiple(true) (if any) debug_assert!(!self.positionals - .values() - .any(|a| a.settings.is_set(ArgSettings::Multiple) && - (a.index as usize != self.positionals.len()) - ), - "Only the positional argument with the highest index may accept multiple values"); + .values() + .any(|a| { + a.settings.is_set(ArgSettings::Multiple) && + (a.index as usize != self.positionals.len()) + }), + "Only the positional argument with the highest index may accept multiple values"); // If it's required we also need to ensure all previous positionals are // required too @@ -441,6 +442,7 @@ impl<'a, 'b> Parser<'a, 'b> where I: Iterator, T: Into { + use osstrext::OsStrExt; debugln!("fn=get_matches_with;"); // First we create the `--help` and `--version` arguments and add them if // necessary @@ -456,9 +458,9 @@ impl<'a, 'b> Parser<'a, 'b> // Is this a new argument, or values from a previous option? debug!("Starts new arg..."); - let starts_new_arg = if arg_os.starts_with(b"-") { + let starts_new_arg = if arg_os._starts_with(b"-") { sdebugln!("Yes"); - !(arg_os.len_() == 1) + !(arg_os._len() == 1) } else { sdebugln!("No"); false @@ -469,13 +471,17 @@ impl<'a, 'b> Parser<'a, 'b> // Does the arg match a subcommand name, or any of it's aliases (if defined) let pos_sc = self.subcommands .iter() - .any(|s| &s.p.meta.name[..] == &*arg_os || + .any(|s| { + &s.p.meta.name[..] == &*arg_os || (s.p.meta.aliases.is_some() && - s.p.meta.aliases - .as_ref() - .unwrap() - .iter() - .any(|&a| a == &*arg_os))); + s.p + .meta + .aliases + .as_ref() + .unwrap() + .iter() + .any(|&a| a == &*arg_os)) + }); if (!starts_new_arg || self.is_set(AppSettings::AllowLeadingHyphen)) && !pos_sc { // Check to see if parsing a value from an option if let Some(nvo) = needs_val_of { @@ -487,8 +493,8 @@ impl<'a, 'b> Parser<'a, 'b> } } } - if arg_os.starts_with(b"--") { - if arg_os.len_() == 2 { + if arg_os._starts_with(b"--") { + if arg_os._len() == 2 { // The user has passed '--' which means only positional args follow no // matter what they start with pos_only = true; @@ -497,7 +503,7 @@ impl<'a, 'b> Parser<'a, 'b> needs_val_of = try!(self.parse_long_arg(matcher, &arg_os)); continue; - } else if arg_os.starts_with(b"-") && arg_os.len_() != 1 { + } else if arg_os._starts_with(b"-") && arg_os._len() != 1 { needs_val_of = try!(self.parse_short_arg(matcher, &arg_os)); if !(needs_val_of.is_none() && self.is_set(AppSettings::AllowLeadingHyphen)) { continue; @@ -535,7 +541,7 @@ impl<'a, 'b> Parser<'a, 'b> sc.create_help_and_version(); return sc._help(); } - subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned()); + subcmd_name = Some(arg_os.to_string_lossy().into_owned()); break; } else if let Some(cdate) = suggestions::did_you_mean(&*arg_os.to_string_lossy(), self.subcommands @@ -555,22 +561,20 @@ impl<'a, 'b> Parser<'a, 'b> parse_positional!(self, p, arg_os, pos_only, pos_counter, matcher); } else { if self.settings.is_set(AppSettings::AllowExternalSubcommands) { - let mut sc_m = ArgMatcher::new(); + let mut args = vec![arg_os.to_owned()]; while let Some(v) = it.next() { let a = v.into(); - if let None = a.to_str() { - if !self.settings.is_set(AppSettings::StrictUtf8) { - return Err( - Error::invalid_utf8(&*self.create_current_usage(matcher)) - ); - } + if !self.settings.is_set(AppSettings::StrictUtf8) && a.to_str().is_none() { + return Err( + Error::invalid_utf8(&*self.create_current_usage(matcher)) + ); } - sc_m.add_val_to("EXTERNAL_SUBCOMMAND", &a); + args.push(a); } matcher.subcommand(SubCommand { - name: "EXTERNAL_SUBCOMMAND".into(), - matches: sc_m.into(), + name: args.join(" "), + matches: ArgMatches::new(), }); } else { return Err(Error::unknown_argument(&*arg_os.to_string_lossy(), @@ -620,15 +624,17 @@ impl<'a, 'b> Parser<'a, 'b> self.subcommands .iter() .filter(|sc| sc.p.meta.aliases.is_some()) - .filter_map(|sc| if sc.p.meta.aliases - .as_ref() - .unwrap() - .iter() - .any(|&a| a == &*pos_sc_name) { - Some(sc.p.meta.name.clone()) - } else { - None - }) + .filter_map(|sc| if sc.p + .meta + .aliases + .as_ref() + .unwrap() + .iter() + .any(|&a| a == &*pos_sc_name) { + Some(sc.p.meta.name.clone()) + } else { + None + }) .next() .expect(INTERNAL_ERROR_MSG) }; @@ -709,7 +715,7 @@ impl<'a, 'b> Parser<'a, 'b> &*sc.p.meta.name)); try!(sc.p.get_matches_with(&mut sc_matcher, it)); matcher.subcommand(SubCommand { - name: sc.p.meta.name.clone(), + name: OsString::from(sc.p.meta.name.clone()), matches: sc_matcher.into(), }); } @@ -967,18 +973,19 @@ impl<'a, 'b> Parser<'a, 'b> matcher: &mut ArgMatcher<'a>, full_arg: &OsStr) -> ClapResult> { + use osstrext::OsStrExt; // maybe here lifetime should be 'a debugln!("fn=parse_long_arg;"); let mut val = None; debug!("Does it contain '='..."); - let arg = if full_arg.contains_byte(b'=') { - let (p0, p1) = full_arg.trim_left_matches(b'-').split_at_byte(b'='); + let arg = if full_arg._contains_byte(b'=') { + let (p0, p1) = full_arg._trim_left_matches(b'-')._split_at_byte(b'='); sdebugln!("Yes '{:?}'", p1); val = Some(p1); p0 } else { sdebugln!("No"); - full_arg.trim_left_matches(b'-') + full_arg._trim_left_matches(b'-') }; if let Some(opt) = self.opts @@ -1015,9 +1022,10 @@ impl<'a, 'b> Parser<'a, 'b> matcher: &mut ArgMatcher<'a>, full_arg: &OsStr) -> ClapResult> { + use osstrext::OsStrExt; debugln!("fn=parse_short_arg;"); // let mut utf8 = true; - let arg_os = full_arg.trim_left_matches(b'-'); + let arg_os = full_arg._trim_left_matches(b'-'); let arg = arg_os.to_string_lossy(); for c in arg.chars() { @@ -1040,9 +1048,9 @@ impl<'a, 'b> Parser<'a, 'b> let i = p[0].as_bytes().len() + 1; let val = if p[1].as_bytes().len() > 0 { debugln!("setting val: {:?} (bytes), {:?} (ascii)", - arg_os.split_at(i).1.as_bytes(), - arg_os.split_at(i).1); - Some(arg_os.split_at(i).1) + arg_os._split_at(i).1.as_bytes(), + arg_os._split_at(i).1); + Some(arg_os._split_at(i).1) } else { None }; @@ -1081,17 +1089,18 @@ impl<'a, 'b> Parser<'a, 'b> opt: &OptBuilder<'a, 'b>, matcher: &mut ArgMatcher<'a>) -> ClapResult> { + use osstrext::OsStrExt; debugln!("fn=parse_opt;"); validate_multiples!(self, opt, matcher); debug!("Checking for val..."); if let Some(fv) = val { - let v = fv.trim_left_matches(b'='); - if !opt.is_set(ArgSettings::EmptyValues) && v.len_() == 0 { + let v = fv._trim_left_matches(b'='); + if !opt.is_set(ArgSettings::EmptyValues) && v._len() == 0 { sdebugln!("Found Empty - Error"); return Err(Error::empty_value(opt, &*self.create_current_usage(matcher))); } - sdebugln!("Found - {:?}, len: {}", v, v.len_()); + sdebugln!("Found - {:?}, len: {}", v, v._len()); try!(self.add_val_to_arg(opt, v, matcher)); } else { sdebugln!("None"); @@ -1114,13 +1123,14 @@ impl<'a, 'b> Parser<'a, 'b> -> ClapResult> where A: AnyArg<'a, 'b> + Display { + use osstrext::OsStrExt; debugln!("fn=add_val_to_arg;"); let mut ret = None; if let Some(delim) = arg.val_delim() { - if val.is_empty_() { + if val._is_empty() { ret = try!(self.add_single_val_to_arg(arg, val, matcher)); } else { - for v in val.split(delim as u32 as u8) { + for v in val._split(delim as u32 as u8) { ret = try!(self.add_single_val_to_arg(arg, v, matcher)); } } @@ -1159,6 +1169,7 @@ impl<'a, 'b> Parser<'a, 'b> -> ClapResult> where A: AnyArg<'a, 'b> + Display { + use osstrext::OsStrExt; debugln!("fn=validate_value; val={:?}", val); if self.is_set(AppSettings::StrictUtf8) && val.to_str().is_none() { return Err(Error::invalid_utf8(&*self.create_current_usage(matcher))); @@ -1172,7 +1183,7 @@ impl<'a, 'b> Parser<'a, 'b> &*self.create_current_usage(matcher))); } } - if !arg.is_set(ArgSettings::EmptyValues) && val.is_empty_() && + if !arg.is_set(ArgSettings::EmptyValues) && val._is_empty() && matcher.contains(&*arg.name()) { return Err(Error::empty_value(arg, &*self.create_current_usage(matcher))); } diff --git a/src/args/arg_matches.rs b/src/args/arg_matches.rs index c86e7cdab8d..bf282d58299 100644 --- a/src/args/arg_matches.rs +++ b/src/args/arg_matches.rs @@ -6,8 +6,7 @@ use std::borrow::Cow; use vec_map; -use args::SubCommand; -use args::MatchedArg; +use args::{SubCommand, SubCommandKey, MatchedArg}; use INVALID_UTF8; /// Used to get information about the arguments that where supplied to the program at runtime by @@ -307,7 +306,7 @@ impl<'a> ArgMatches<'a> { /// ``` pub fn is_present>(&self, name: S) -> bool { if let Some(ref sc) = self.subcommand { - if sc.name == name.as_ref() { + if sc.name.to_str().unwrap_or("") == name.as_ref() { return true; } } diff --git a/src/args/mod.rs b/src/args/mod.rs index b3daa9fa637..dbcc528ea14 100644 --- a/src/args/mod.rs +++ b/src/args/mod.rs @@ -7,6 +7,7 @@ pub use self::matched_arg::MatchedArg; pub use self::group::ArgGroup; pub use self::any_arg::{AnyArg, DispOrder}; pub use self::settings::ArgSettings; +pub use self::subcommand::SubCommandKey; mod arg; pub mod any_arg; diff --git a/src/args/subcommand.rs b/src/args/subcommand.rs index e18e6036b86..d5a021747c4 100644 --- a/src/args/subcommand.rs +++ b/src/args/subcommand.rs @@ -1,3 +1,6 @@ +use std::ffi::OsString; +use std::ffi::OsStr; + #[cfg(feature = "yaml")] use yaml_rust::Yaml; @@ -28,7 +31,7 @@ use ArgMatches; #[derive(Debug, Clone)] pub struct SubCommand<'a> { #[doc(hidden)] - pub name: String, + pub name: OsString, #[doc(hidden)] pub matches: ArgMatches<'a>, } @@ -46,8 +49,9 @@ impl<'a> SubCommand<'a> { /// SubCommand::with_name("config")) /// # ; /// ``` - pub fn with_name<'b>(name: &str) -> App<'a, 'b> { - App::new(name) + pub fn with_name<'b, T>(t: T) -> App<'a, 'b> + where T: AsRef { + App::new(t.as_ref()) } /// Creates a new instance of a subcommand from a YAML (.yml) document @@ -64,3 +68,21 @@ impl<'a> SubCommand<'a> { App::from_yaml(yaml) } } + +// Users shouldn't have to worry about this, used internally to give a from_str which doesn't +// return a `Result` +#[doc(hidden)] +pub trait SubCommandKey { + fn from_os_str(s: &OsStr) -> Self; + fn external(args: Vec) -> Option where Self: Sized; +} + +impl<'a> SubCommandKey for &'a str { + fn from_os_str(s: &OsStr) -> Self { + use std::mem; + unsafe { mem::transmute(s) } // should not actually be called in the real world + } + fn external(_: Vec) -> Option { + None + } +} diff --git a/src/lib.rs b/src/lib.rs index 9571b346001..2c03b4fae7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -407,9 +407,9 @@ extern crate strsim; extern crate ansi_term; #[cfg(feature = "yaml")] extern crate yaml_rust; -#[cfg(all(feature = "wrap_help", not(target_os = "windows")))] +#[cfg(all(feature = "wrap_help", not(windows)))] extern crate libc; -#[cfg(all(feature = "wrap_help", not(target_os = "windows")))] +#[cfg(all(feature = "wrap_help", not(windows)))] extern crate unicode_width; #[macro_use] extern crate bitflags; @@ -421,6 +421,8 @@ pub use args::{Arg, ArgGroup, ArgMatches, ArgSettings, SubCommand, Values, OsVal pub use app::{App, AppSettings}; pub use fmt::Format; pub use errors::{Error, ErrorKind, Result}; +pub use args::SubCommandKey; +pub use osstrext::OsStrExt; #[macro_use] mod macros; @@ -430,9 +432,10 @@ mod usage_parser; mod fmt; mod suggestions; mod errors; -mod osstringext; mod term; mod strext; +mod vecext; +mod osstrext; const INTERNAL_ERROR_MSG: &'static str = "Fatal internal error. Please consider filing a bug \ report at https://github.com/kbknapp/clap-rs/issues"; diff --git a/src/macros.rs b/src/macros.rs index 77cd13b7bf4..b669a2c3444 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -533,9 +533,407 @@ macro_rules! clap_app { // Start the magic ($name:ident => $($tail:tt)*) => {{ clap_app!{ @app ($crate::App::new(stringify!($name))) $($tail)*} - }}; +}}; } +/// A convienience macro for defining enums that can be used to access `SubCommand`s. By using this +/// macro, all traits are implemented automatically. The traits implemented are, `SubCommandKey` +/// (an internal trait one needn't worry about), `Display`, `Into<&'static str>` and `AsRef`. +/// This macro also implements a `variants()` function which returns an array of `&'static str`s +/// containing the variant names. +/// +/// There are two ways to use this macro, in an as-is scenario where the variants one defines are +/// exaclty how the subcommands are displayed to the end user. There is also an alternative way +/// where the actual display of the subcommands can be changed. Examples of both are bellow. +/// +/// This allows rustc to do some checking for you, i.e. if you add another +/// subcommand later, but forget to check for it, rustc will complain about +/// NonExaustive matches. Likewise, if you make a simple spelling or typing +/// error. +/// +/// # External Subcommands +/// +/// FIXME: add docs for external subcomands with examples +/// +/// **Pro Tip:** It's good practice to make the name of the enum the same as +/// the parent command, and the variants the names of the actual subcommands +/// +/// # Examples +/// +/// ```rust +/// # #[macro_use] +/// # extern crate clap; +/// # use clap::{App, SubCommand}; +/// // Note lowercase variants, the subcommand will be exactly as typed here +/// subcommands!{ +/// enum MyProg { +/// show, +/// delete, +/// make +/// } +/// } +/// +/// // Alternatively, if you wish to have variants which display +/// // differently, or contain hyphens ("-") one can use this variation of +/// // the macro +/// subcommands!{ +/// enum MyProgAlt { +/// Show => "show", +/// Delete => "delete", +/// DoStuff => "do-stuff" +/// } +/// } +/// +/// fn main() { +/// let m = App::new("myprog") +/// .subcommand(SubCommand::with_name(MyProg::show)) +/// .subcommand(SubCommand::with_name(MyProg::delete)) +/// .subcommand(SubCommand::with_name(MyProg::make)) +/// .get_matches_from(vec!["myprog", "show"]); +/// +/// match m.subcommand() { +/// Some((MyProg::show, _)) => println!("'myprog show' was used"), +/// Some((MyProg::delete, _)) => println!("'myprog delete' was used"), +/// Some((MyProg::make, _)) => println!("'myprog make' was used"), +/// None => println!("No subcommand was used"), +/// } +/// } +/// ``` +#[macro_export] +macro_rules! subcommands { + (@as_item $($i:item)*) => ($($i)*); + (@impls_s_ext ( $($tts:tt)* ) -> ($e:ident, $($v:ident=>$s:expr),+)) => { + subcommands!(@as_item + #[allow(unused_imports)] + use ::clap::SubCommandKey; + #[derive(PartialEq)] + #[allow(non_camel_case_types)] + $($tts)* + impl<'a> ::clap::SubCommandKey for $e { + fn from_os_str(s: &::std::ffi::OsStr) -> Self { + use ::clap::OsStrExt; + match &s.to_string_lossy()[..] { + $($s => $e::$v),+, + _ => $e::External((*s)._split(b' ').map(ToOwned::to_owned).collect::>()), + } + } + fn external(args: Vec<::std::ffi::OsString>) -> Option { + Some($e::External(args)) + } + } + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + $e::External(ref args) => write!(f, "{}", args.iter().map(|a| a.to_string_lossy()).collect::>().join(" ")), + $($e::$v => write!(f, $s),)+ + } + } + } + impl ::std::convert::AsRef for $e { + fn as_ref(&self) -> &'static str { + match *self { + $e::External(_) => "External", + $($e::$v => $s,)+ + } + } + } + impl<'a> ::std::convert::Into<&'static str> for $e { + fn into(self) -> &'static str { + match self { + $e::External(_) => "External", + $($e::$v => $s,)+ + } + } + } + impl $e { + #[allow(dead_code)] + pub fn variants() -> [&'static str; _clap_count_exprs!("External", $(stringify!($v)),+)] { + [ + "External", + $(stringify!($s),)+ + ] + } + }); + }; + (@impls_s ( $($tts:tt)* ) -> ($e:ident, $($v:ident=>$s:expr),+)) => { + subcommands!(@as_item + #[allow(unused_imports)] + use ::clap::SubCommandKey; + #[derive(PartialEq)] + #[allow(non_camel_case_types)] + $($tts)* + impl<'a> ::clap::SubCommandKey for $e { + fn from_os_str(s: &::std::ffi::OsStr) -> Self { + match &s.to_string_lossy()[..] { + $($s => $e::$v),+, + _ => unreachable!(), + } + } + fn external(_: Vec<::std::ffi::OsString>) -> Option { + None + } + } + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + $($e::$v => write!(f, $s),)+ + } + } + } + impl ::std::convert::AsRef for $e { + fn as_ref(&self) -> &'static str { + match *self { + $($e::$v => $s,)+ + } + } + } + impl<'a> ::std::convert::Into<&'static str> for $e { + fn into(self) -> &'static str { + match self { + $($e::$v => $s,)+ + } + } + } + impl $e { + #[allow(dead_code)] + pub fn variants() -> [&'static str; _clap_count_exprs!($(stringify!($v)),+)] { + [ + $(stringify!($s),)+ + ] + } + }); + }; + (@impls_ext ( $($tts:tt)* ) -> ($e:ident, $($v:ident),+)) => { + subcommands!(@as_item + #[allow(unused_imports)] + use ::clap::SubCommandKey; + #[derive(PartialEq)] + #[allow(non_camel_case_types)] + $($tts)* + impl<'a> ::clap::SubCommandKey for $e { + fn from_os_str(s: &::std::ffi::OsStr) -> Self { + use ::clap::OsStrExt; + match &s.to_string_lossy()[..] { + $($s => $e::$v),+, + _ => $e::External((*s)._split(b' ').map(ToOwned::to_owned).collect::>()), + } + } + fn external(args: Vec<::std::ffi::OsString>) -> Option { + Some($e::External(args)) + } + } + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + $e::External(ref args) => write!(f, "{}", args.iter().map(|a| a.to_string_lossy()).collect::>().join(" ")), + $($e::$v => write!(f, stringify!($v)),)+ + } + } + } + impl<'a> ::std::convert::Into<&'static str> for $e { + fn into(self) -> &'static str { + match self { + $e::External(_) => "External", + $($e::$v => $s,)+ + } + } + } + impl ::std::convert::AsRef for $e { + fn as_ref(&self) -> &'static str { + match *self { + $e::External(_) => "External", + $($e::$v => stringify!($v),)+ + } + } + } + impl $e { + #[allow(dead_code)] + pub fn variants() -> [&'static str; _clap_count_exprs!("External", $(stringify!($v)),+)] { + [ + "External", + $(stringify!($v),)+ + ] + } + }); + }; + (@impls ( $($tts:tt)* ) -> ($e:ident, $($v:ident),+)) => { + subcommands!(@as_item + #[allow(unused_imports)] + use ::clap::SubCommandKey; + #[derive(PartialEq)] + #[allow(non_camel_case_types)] + $($tts)* + impl<'a> ::clap::SubCommandKey for $e { + fn from_os_str(s: &::std::ffi::OsStr) -> Self { + match &s.to_string_lossy()[..] { + $($s => $e::$v),+, + _ => unreachable!(), + } + } + fn external(_: Vec<::std::ffi::OsString>) -> Option { + None + } + } + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + $($e::$v => write!(f, stringify!($v)),)+ + } + } + } + impl<'a> ::std::convert::Into<&'static str> for $e { + fn into(self) -> &'static str { + match self { + $($e::$v => $s,)+ + } + } + } + impl ::std::convert::AsRef for $e { + fn as_ref(&self) -> &'static str { + match *self { + $($e::$v => stringify!($v),)+ + } + } + } + impl $e { + #[allow(dead_code)] + pub fn variants() -> [&'static str; _clap_count_exprs!($(stringify!($v)),+)] { + [ + $(stringify!($v),)+ + ] + } + }); + }; + (#[$($m:meta),+] pub enum $e:ident { External, $($v:ident=>$s:expr),+ } ) => { + subcommands!(@impls_s_ext + (#[$($m),+] + pub enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v=>$s),+) + ); + }; + (#[$($m:meta),+] enum $e:ident { External, $($v:ident=>$s:expr),+ } ) => { + subcommands!(@impls_s_ext + (#[$($m),+] + enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v=>$s:expr),+) + ); + }; + (#[$($m:meta),+] pub enum $e:ident { External, $($v:ident),+ } ) => { + subcommands!(@impls_ext + (#[$($m),+] + pub enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v),+) + ); + }; + (#[$($m:meta),+] enum $e:ident { External, $($v:ident),+ } ) => { + subcommands!(@impls_ext + (#[$($m),+] + enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v),+) + ); + }; + (#[$($m:meta),+] pub enum $e:ident { $($v:ident=>$s:expr),+ } ) => { + subcommands!(@impls_s + (#[$($m),+] + pub enum $e { + $($v),+, + }) -> ($e, $($v=>$s),+) + ); + }; + (#[$($m:meta),+] enum $e:ident { $($v:ident=>$s:expr),+ } ) => { + subcommands!(@impls_s + (#[$($m),+] + enum $e { + $($v),+, + }) -> ($e, $($v=>$s:expr),+) + ); + }; + (#[$($m:meta),+] pub enum $e:ident { $($v:ident),+ } ) => { + subcommands!(@impls + (#[$($m),+] + pub enum $e { + $($v),+, + }) -> ($e, $($v),+) + ); + }; + (#[$($m:meta),+] enum $e:ident { $($v:ident),+ } ) => { + subcommands!(@impls + (#[$($m),+] + enum $e { + $($v),+, + }) -> ($e, $($v),+) + ); + }; + (pub enum $e:ident { External, $($v:ident=>$s:expr),+ } ) => { + subcommands!(@impls_s_ext + (pub enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v=>$s),+) + ); + }; + (pub enum $e:ident { External, $($v:ident),+ } ) => { + subcommands!(@impls_ext + (pub enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v),+) + ); + }; + (pub enum $e:ident { $($v:ident=>$s:expr),+ } ) => { + subcommands!(@impls_s + (pub enum $e { + $($v),+, + }) -> ($e, $($v=>$s),+) + ); + }; + (pub enum $e:ident { $($v:ident),+ } ) => { + subcommands!(@impls + (pub enum $e { + $($v),+, + }) -> ($e, $($v),+) + ); + }; + (enum $e:ident { External, $($v:ident=>$s:expr),+ } ) => { + subcommands!(@impls_s_ext + (enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v=>$s),+) + ); + }; + (enum $e:ident { External, $($v:ident),+ } ) => { + subcommands!(@impls_ext + (enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v),+) + ); + }; + (enum $e:ident { $($v:ident=>$s:expr),+ } ) => { + subcommands!(@impls_s + (enum $e { + $($v),+, + }) -> ($e, $($v=>$s),+) + ); + }; + (enum $e:ident { $($v:ident),+ } ) => { + subcommands!(@impls + (enum $e { + $($v),+, + }) -> ($e, $($v),+) + ); + }; + } + macro_rules! impl_settings { ($n:ident, $($v:ident => $c:ident),+) => { pub fn set(&mut self, s: $n) { diff --git a/src/osstringext.rs b/src/osstrext.rs similarity index 70% rename from src/osstringext.rs rename to src/osstrext.rs index f926b789b99..fc76534282d 100644 --- a/src/osstringext.rs +++ b/src/osstrext.rs @@ -1,49 +1,36 @@ use std::ffi::OsStr; -#[cfg(not(target_os = "windows"))] -use std::os::unix::ffi::OsStrExt; +#[cfg(not(windows))] +use std::os::unix::ffi::OsStrExt as StdOsStrExt; -#[cfg(target_os = "windows")] +#[cfg(windows)] use INVALID_UTF8; -#[cfg(target_os = "windows")] -trait OsStrExt3 { +#[doc(hidden)] +pub trait OsStrExt { + fn _starts_with(&self, s: &[u8]) -> bool; + fn _split_at_byte(&self, b: u8) -> (&OsStr, &OsStr); + fn _split_at(&self, i: usize) -> (&OsStr, &OsStr); + fn _trim_left_matches(&self, b: u8) -> &OsStr; + fn _len(&self) -> usize; + fn _contains_byte(&self, b: u8) -> bool; + fn _is_empty(&self) -> bool; + fn _split(&self, b: u8) -> OsSplit; + #[cfg(windows)] fn from_bytes(b: &[u8]) -> &Self; + #[cfg(windows)] fn as_bytes(&self) -> &[u8]; } -#[doc(hidden)] -pub trait OsStrExt2 { - fn starts_with(&self, s: &[u8]) -> bool; - fn split_at_byte(&self, b: u8) -> (&OsStr, &OsStr); - fn split_at(&self, i: usize) -> (&OsStr, &OsStr); - fn trim_left_matches(&self, b: u8) -> &OsStr; - fn len_(&self) -> usize; - fn contains_byte(&self, b: u8) -> bool; - fn is_empty_(&self) -> bool; - fn split(&self, b: u8) -> OsSplit; -} - -#[cfg(target_os = "windows")] -impl OsStrExt3 for OsStr { - fn from_bytes(b: &[u8]) -> &Self { - use std::mem; - unsafe { mem::transmute(b) } - } - fn as_bytes(&self) -> &[u8] { - self.to_str().map(|s| s.as_bytes()).expect(INVALID_UTF8) - } -} - -impl OsStrExt2 for OsStr { - fn starts_with(&self, s: &[u8]) -> bool { - self.as_bytes().starts_with(s) +impl OsStrExt for OsStr { + fn _starts_with(&self, s: &[u8]) -> bool { + Self::as_bytes(self).starts_with(s) } - fn is_empty_(&self) -> bool { - self.as_bytes().is_empty() + fn _is_empty(&self) -> bool { + Self::as_bytes(self).is_empty() } - fn contains_byte(&self, byte: u8) -> bool { + fn _contains_byte(&self, byte: u8) -> bool { for b in self.as_bytes() { if b == &byte { return true; @@ -52,17 +39,17 @@ impl OsStrExt2 for OsStr { false } - fn split_at_byte(&self, byte: u8) -> (&OsStr, &OsStr) { + fn _split_at_byte(&self, byte: u8) -> (&OsStr, &OsStr) { for (i, b) in self.as_bytes().iter().enumerate() { if b == &byte { return (&OsStr::from_bytes(&self.as_bytes()[..i]), &OsStr::from_bytes(&self.as_bytes()[i + 1..])); } } - (&*self, &OsStr::from_bytes(&self.as_bytes()[self.len_()..self.len_()])) + (&*self, &OsStr::from_bytes(&self.as_bytes()[self._len()..self._len()])) } - fn trim_left_matches(&self, byte: u8) -> &OsStr { + fn _trim_left_matches(&self, byte: u8) -> &OsStr { for (i, b) in self.as_bytes().iter().enumerate() { if b != &byte { return &OsStr::from_bytes(&self.as_bytes()[i..]); @@ -71,21 +58,30 @@ impl OsStrExt2 for OsStr { &*self } - fn split_at(&self, i: usize) -> (&OsStr, &OsStr) { + fn _split_at(&self, i: usize) -> (&OsStr, &OsStr) { (&OsStr::from_bytes(&self.as_bytes()[..i]), &OsStr::from_bytes(&self.as_bytes()[i..])) } - fn len_(&self) -> usize { + fn _len(&self) -> usize { self.as_bytes().len() } - fn split(&self, b: u8) -> OsSplit { + fn _split(&self, b: u8) -> OsSplit { OsSplit { sep: b, val: self.as_bytes(), pos: 0, } } + #[cfg(windows)] + fn from_bytes(b: &[u8]) -> &Self { + use std::mem; + unsafe { mem::transmute(b) } + } + #[cfg(windows)] + fn as_bytes(&self) -> &[u8] { + self.to_str().map(|s| s.as_bytes()).expect(INVALID_UTF8) + } } #[doc(hidden)] diff --git a/src/vecext.rs b/src/vecext.rs new file mode 100644 index 00000000000..58e00c51337 --- /dev/null +++ b/src/vecext.rs @@ -0,0 +1,60 @@ +use std::ffi::{OsString, OsStr}; +#[cfg(windows)] +use osstrext::OsStrExt; +#[cfg(not(windows))] +use std::os::unix::ffi::OsStrExt; + +pub trait VecExt { + fn join(&self, sep: &str) -> T; +} + +#[cfg(not(windows))] +impl VecExt for Vec { + fn join(&self, sep: &str) -> OsString { + use std::os::unix::ffi::OsStringExt; + use osstrext::OsStrExt; + let size = self.iter().fold(0, |acc, v| acc + v._len()); + let mut result = Vec::with_capacity(size + self.len()); + let mut first = true; + for v in self { + if first { + first = false + } else { + result.extend_from_slice(sep.as_bytes()); + } + result.extend_from_slice(OsStr::as_bytes(v.as_os_str())) + } + OsString::from_vec(result) + } +} + +#[cfg(windows)] +impl VecExt for Vec { + fn join(&self, sep: &str) -> OsString { + use osstrext::OsStrExt; + let mut result = OsString::new(); + let mut first = true; + for v in self.split(b" ").iter() { + if first { + first = false + } else { + result.push(sep.as_bytes()); + } + result.push(v) + } + OsString::from_vec(result) + } +} + +#[cfg(test)] +mod test { + use std::ffi::OsString; + use super::VecExt; + + #[test] + fn vec_ext_join() { + let vec = vec![OsString::from("one"), OsString::from("two"), OsString::from("three")]; + let oss = vec.join(" "); + assert_eq!("one two three", &oss.to_string_lossy()[..]); + } +} From 9f50187eeb7cf38cf390024333ab5038c4c442d1 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Thu, 12 May 2016 19:31:18 -0400 Subject: [PATCH 02/18] examples: updates app settings to use non-stringly typed subcommand --- examples/16_app_settings.rs | 14 ++++-- src/macros.rs | 97 ++++++++++++++++++++++++++++++++----- 2 files changed, 95 insertions(+), 16 deletions(-) diff --git a/examples/16_app_settings.rs b/examples/16_app_settings.rs index ab1d1850b9c..c5810c28029 100644 --- a/examples/16_app_settings.rs +++ b/examples/16_app_settings.rs @@ -1,8 +1,16 @@ +#[macro_use] extern crate clap; use clap::{App, AppSettings, SubCommand}; +subcommands!{ + enum MyApp { + Test => "test" + } +} + fn main() { + use MyApp::*; // You can use AppSettings to change the application level behavior of clap. .setting() function // of App struct takes AppSettings enum as argument. There is also .settings() function which // takes slice of AppSettings enum. You can learn more about AppSettings in the documentation, @@ -19,7 +27,7 @@ fn main() { // Required positional argument called input. This // will be only required if subcommand is not present. - .subcommand(SubCommand::with_name("test") + .subcommand(SubCommand::with_name(Test) .about("does some testing")) // if program is invoked with subcommand, you do not // need to specify the argument anymore due to @@ -35,7 +43,7 @@ fn main() { } match matches.subcommand() { - ("test", _) => println!("The 'test' subcommand was used"), - _ => unreachable!() + Some((Test, _)) => println!("The 'test' subcommand was used"), + None => println!("No subcommand was used"), } } diff --git a/src/macros.rs b/src/macros.rs index b669a2c3444..0b1d1f3d495 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -551,15 +551,31 @@ macro_rules! clap_app { /// NonExaustive matches. Likewise, if you make a simple spelling or typing /// error. /// +/// **Pro Tip:** It's good practice to make the name of the enum the same as +/// the parent command, and the variants the names of the actual subcommands +/// /// # External Subcommands /// -/// FIXME: add docs for external subcomands with examples +/// If you wish to support external subcommands, there are two simple things one must do. First, +/// when using the `subcommands!` macro, the **first** variant you name, **must** be `External`, +/// this tells the macro to generate all the appropriate portions to support external subcommands. +/// Second, you must use the `AppSettings::AllowExternalSubcommands` setting. /// -/// **Pro Tip:** It's good practice to make the name of the enum the same as -/// the parent command, and the variants the names of the actual subcommands +/// After doing these two things, if a possible external subcommand is recognized, `clap` will +/// return the `External(Vec)` variant. The wrapped `Vec` contains the args that were +/// passed to the possible external subcommand (including the subcommand itself). Thse are stored +/// as `OsString`s since it's possible contain invalid UTF-8 code points on some platforms. +/// +/// **Pro Tip**: If you wish to get `&str`s instead and you're *sure* they won't contain invalid +/// UTF-8, or you don't wish to support invalid UTF-8, it's as simple as using the following +/// iterator chain on the returned `Vec`: `v.iter().map(|s| s.to_str().expect("Invalid +/// UTF-8")).collect::>()` /// /// # Examples /// +/// First, an example showing the most basic use of the macro. (i.e. enum variants are used +/// literally) +/// /// ```rust /// # #[macro_use] /// # extern crate clap; @@ -573,28 +589,83 @@ macro_rules! clap_app { /// } /// } /// -/// // Alternatively, if you wish to have variants which display -/// // differently, or contain hyphens ("-") one can use this variation of -/// // the macro +/// fn main() { +/// let m = App::new("myprog") +/// .subcommand(SubCommand::with_name(MyProg::show)) +/// .subcommand(SubCommand::with_name(MyProg::delete)) +/// .subcommand(SubCommand::with_name(MyProg::make)) +/// .get_matches_from(vec!["myprog", "show"]); +/// +/// match m.subcommand() { +/// Some((MyProg::show, _)) => println!("'myprog show' was used"), +/// Some((MyProg::delete, _)) => println!("'myprog delete' was used"), +/// Some((MyProg::make, _)) => println!("'myprog make' was used"), +/// None => println!("No subcommand was used"), +/// } +/// } +/// ``` +/// +/// Next, if you wish to support subcommands with things like hyphen characters, or don't like having +/// non-camel-case types, a second version of the macro exists which allows specifying a literal subcommand +/// which gets associated with a enum variant. +/// +/// ```rust +/// # #[macro_use] +/// # extern crate clap; +/// # use clap::{App, SubCommand}; /// subcommands!{ -/// enum MyProgAlt { +/// enum MyProg { /// Show => "show", /// Delete => "delete", /// DoStuff => "do-stuff" /// } /// } +/// fn main() { +/// use MyProg::*; +/// let m = App::new("myprog") +/// .subcommand(SubCommand::with_name(Show)) +/// .subcommand(SubCommand::with_name(Delete)) +/// .subcommand(SubCommand::with_name(DoStuff)) +/// .get_matches_from(vec!["myprog", "show"]); +/// +/// match m.subcommand() { +/// Some((Show, _)) => println!("'myprog show' was used"), +/// Some((Delete, _)) => println!("'myprog delete' was used"), +/// Some((DoStuff, _)) => println!("'myprog make' was used"), +/// None => println!("No subcommand was used"), +/// } +/// } +/// ``` +/// +/// Finally, if one wishes to support external subcommands, simply ensure the first variant is called +/// `External` and the appropriate `AppSettings` variant is used. /// +/// ```rust +/// # #[macro_use] +/// # extern crate clap; +/// # use clap::{App, SubCommand, AppSettings}; +/// subcommands!{ +/// enum MyProg { +/// External, +/// Show => "show", +/// Delete => "delete", +/// DoStuff => "do-stuff" +/// } +/// } /// fn main() { +/// use MyProg::*; /// let m = App::new("myprog") -/// .subcommand(SubCommand::with_name(MyProg::show)) -/// .subcommand(SubCommand::with_name(MyProg::delete)) -/// .subcommand(SubCommand::with_name(MyProg::make)) +/// .subcommand(SubCommand::with_name(Show)) +/// .subcommand(SubCommand::with_name(Delete)) +/// .subcommand(SubCommand::with_name(DoStuff)) +/// .setting(AppSettings::AllowExternalSubcommands) /// .get_matches_from(vec!["myprog", "show"]); /// /// match m.subcommand() { -/// Some((MyProg::show, _)) => println!("'myprog show' was used"), -/// Some((MyProg::delete, _)) => println!("'myprog delete' was used"), -/// Some((MyProg::make, _)) => println!("'myprog make' was used"), +/// Some((Show, _)) => println!("'myprog show' was used"), +/// Some((Delete, _)) => println!("'myprog delete' was used"), +/// Some((DoStuff, _)) => println!("'myprog make' was used"), +/// Some((External(ref v), _)) => println!("An external subcommand: {:?}", v), /// None => println!("No subcommand was used"), /// } /// } From 80afc0a512868cafa08b9fce72c00c31e0d980f8 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Thu, 12 May 2016 19:31:55 -0400 Subject: [PATCH 03/18] chore: increase version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 76e1e723bfb..eb611b5f761 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clap" -version = "2.5.1" +version = "3.0.0" authors = ["Kevin K. "] exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"] description = "A simple to use, efficient, and full featured Command Line Argument Parser" From 25365548bcd4146fc7ed43073becc17d688a579b Mon Sep 17 00:00:00 2001 From: Kevin K Date: Thu, 12 May 2016 20:11:15 -0400 Subject: [PATCH 04/18] tests: updates tests to new 3.x features --- tests/subcommands.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/subcommands.rs b/tests/subcommands.rs index 2d055461ef7..9585d3c257b 100644 --- a/tests/subcommands.rs +++ b/tests/subcommands.rs @@ -17,7 +17,7 @@ fn subcommand() { .arg(Arg::with_name("other").long("other")) .get_matches_from(vec!["myprog", "some", "--test", "testing"]); - assert_eq!(m.subcommand_name().unwrap(), "some"); + assert_eq!(m.subcommand_name::<&str>().unwrap(), "some"); let sub_m = m.subcommand_matches("some").unwrap(); assert!(sub_m.is_present("test")); assert_eq!(sub_m.value_of("test").unwrap(), "testing"); @@ -35,7 +35,7 @@ fn subcommand_none_given() { .arg(Arg::with_name("other").long("other")) .get_matches_from(vec![""]); - assert!(m.subcommand_name().is_none()); + assert!(m.subcommand_name::<&str>().is_none()); } #[test] @@ -56,7 +56,7 @@ fn subcommand_multiple() { assert!(m.subcommand_matches("some").is_some()); assert!(m.subcommand_matches("add").is_none()); - assert_eq!(m.subcommand_name().unwrap(), "some"); + assert_eq!(m.subcommand_name::<&str>().unwrap(), "some"); let sub_m = m.subcommand_matches("some").unwrap(); assert!(sub_m.is_present("test")); assert_eq!(sub_m.value_of("test").unwrap(), "testing"); From ff8382419af4347ca9be08ac88deba95b3032b55 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Thu, 19 May 2016 21:16:54 -0400 Subject: [PATCH 05/18] tests: fixes failing doc tests --- src/app/settings.rs | 4 +- src/args/arg_matches.rs | 86 +++++++++++++++++++++++++++++++++-------- src/macros.rs | 72 ++++++++++++++-------------------- 3 files changed, 101 insertions(+), 61 deletions(-) diff --git a/src/app/settings.rs b/src/app/settings.rs index 752ddb27899..9f53e1c6f18 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -253,7 +253,7 @@ pub enum AppSettings { /// /// **NOTE:** If the user specifies arguments at runtime, but no subcommand the help text will /// still be displayed and exit. If this is *not* the desired result, consider using - /// [`AppSettings::ArgRequiredElseHelp`] instead. + /// `ArgRequiredElseHelp` instead. /// /// # Examples /// @@ -453,6 +453,8 @@ pub enum AppSettings { 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 diff --git a/src/args/arg_matches.rs b/src/args/arg_matches.rs index bf282d58299..a0d6d09880a 100644 --- a/src/args/arg_matches.rs +++ b/src/args/arg_matches.rs @@ -389,7 +389,7 @@ impl<'a> ArgMatches<'a> { /// [`ArgMatches`]: ./struct.ArgMatches.html pub fn subcommand_matches>(&self, name: S) -> Option<&ArgMatches<'a>> { if let Some(ref s) = self.subcommand { - if s.name == name.as_ref() { + if s.name.to_str().unwrap_or("") == name.as_ref() { return Some(&s.matches); } } @@ -463,19 +463,20 @@ impl<'a> ArgMatches<'a> { /// /// # Examples /// - /// ```no_run + /// ```rust /// # use clap::{App, Arg, SubCommand}; /// let app_m = App::new("git") /// .subcommand(SubCommand::with_name("clone")) /// .subcommand(SubCommand::with_name("push")) /// .subcommand(SubCommand::with_name("commit")) - /// .get_matches(); + /// .get_matches_from(vec!["git", "clone"]); /// /// match app_m.subcommand() { - /// ("clone", Some(sub_m)) => {}, // clone was used - /// ("push", Some(sub_m)) => {}, // push was used - /// ("commit", Some(sub_m)) => {}, // commit was used - /// _ => {}, // Either no subcommand or one not tested for... + /// Some(("clone", sub_m)) => {}, // clone was used + /// Some(("push", sub_m)) => {}, // push was used + /// Some(("commit", sub_m)) => {}, // commit was used + /// None => {}, // No subcommand used + /// _ => {}, // unreachable /// } /// ``` /// @@ -483,29 +484,80 @@ impl<'a> ArgMatches<'a> { /// In these cases you can't know the subcommand name ahead of time, so use a variable instead /// with pattern matching! /// + /// Because of how subcommands are parsed, there are the two ways in which you can creat + /// subcommands, either by using [`&str`] slices, or by using the newer and far superior + /// [enum macros]. + /// + /// This first method shows using the enum macros. + /// /// ```rust + /// # use std::ffi::OsString; + /// # #[macro_use] + /// # extern crate clap; /// # use clap::{App, AppSettings}; + /// # fn main() { + /// + /// // Here we support two subcomamnds, "do-stuff" and some external command unknown at compile + /// // time. + /// // Note the `=> "do-stuff"` alternatively we could have left this part off and the user + /// // would have to type our variant exactly `DoStuff` + /// subcommands! { + /// pub enum MyProg { + /// External, + /// DoStuff => "do-stuff" + /// } + /// } + /// /// // Assume there is an external subcommand named "subcmd" - /// let app_m = App::new("myprog") + /// let m = App::new("myprog") /// .setting(AppSettings::AllowExternalSubcommands) /// .get_matches_from(vec![ - /// "myprog", "subcmd", "--option", "value", "-fff", "--flag" + /// "myprog", "subcmd", "--option", "value" /// ]); /// - /// // All trailing arguments will be stored under the subcommand's sub-matches using a value - /// // of the runtime subcommand name (in this case "subcmd") - /// match app_m.subcommand() { - /// (external, Some(sub_m)) => { - /// let ext_args: Vec<&str> = sub_m.values_of(external).unwrap().collect(); - /// assert_eq!(ext_args, ["--option", "value", "-fff", "--flag"]); + /// // The external subcommand and all trailing arguments will be stored as an Vec + /// // inside the `External` variant created by the macro + /// match m.subcommand() { + /// Some((MyProg::External(ref args), _)) => { + /// assert_eq!(args, + /// &[OsString::from("subcmd"), + /// OsString::from("--option"), + /// OsString::from("value") + /// ]); + /// }, + /// Some((MyProg::DoStuff, _)) => println!("do-stuff used..."), + /// None => {}, // no subcommand was used + /// } + /// # } + /// ``` + /// + /// This second method shows using the older `&str` method + /// + /// ```rust + /// # use clap::{App, AppSettings}; + /// // Assume there is an external subcommand named "subcmd" + /// let m = App::new("myprog") + /// .setting(AppSettings::AllowExternalSubcommands) + /// .get_matches_from(vec![ + /// "myprog", "subcmd", "--option", "value" + /// ]); + /// + /// // The external subcommand and all trailing arguments will be stored as a &str + /// // notice that we must specify a type hint + /// match m.subcommand::<&str>() { + /// Some((args, _)) => { + /// assert_eq!(args, "subcmd --option value"); /// }, /// _ => {}, /// } /// ``` + /// [`&str`]: https://doc.rust-lang.org/std/primitive.str.html + /// [enum macros]: ./macro.subcommands!.html /// [`ArgMatches::subcommand_matches`]: ./struct.ArgMatches.html#method.subcommand_matches /// [`ArgMatches::subcommand_name`]: ./struct.ArgMatches.html#method.subcommand_name - pub fn subcommand(&self) -> (&str, Option<&ArgMatches<'a>>) { - self.subcommand.as_ref().map_or(("", None), |sc| (&sc.name[..], Some(&sc.matches))) + pub fn subcommand(&self) -> Option<(S, &ArgMatches<'a>)> + where S: SubCommandKey { + self.subcommand.as_ref().map_or(None, |sc| Some((S::from_os_str(&sc.name), &sc.matches))) } /// Returns a string slice of the usage statement for the [`App`] or [`SubCommand`] diff --git a/src/macros.rs b/src/macros.rs index 0b1d1f3d495..1e41bde27f5 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -35,19 +35,18 @@ macro_rules! load_yaml { /// /// # Examples /// -/// ```no_run -/// # #[macro_use] -/// # extern crate clap; +/// ``` +/// # #[macro_use] extern crate clap; /// # use clap::App; /// # fn main() { /// let matches = App::new("myapp") /// .arg_from_usage("[length] 'Set the length to use as a pos whole num, i.e. 20'") -/// .get_matches(); +/// .get_matches_from(vec!["myapp", "8"]); /// /// let len = value_t!(matches.value_of("length"), u32).unwrap_or_else(|e| e.exit()); /// let also_len = value_t!(matches, "length", u32).unwrap_or_else(|e| e.exit()); /// -/// println!("{} + 2: {}", len, len + 2); +/// assert_eq!(10, len + 2); /// # } /// ``` /// [`std::str::FromStr`]: https://doc.rust-lang.org/std/str/trait.FromStr.html @@ -80,19 +79,19 @@ macro_rules! value_t { /// /// # Examples /// -/// ```no_run +/// ``` /// # #[macro_use] /// # extern crate clap; /// # use clap::App; /// # fn main() { /// let matches = App::new("myapp") /// .arg_from_usage("[length] 'Set the length to use as a pos whole num, i.e. 20'") -/// .get_matches(); +/// .get_matches_from(vec!["myapp", "8"]); /// /// let len = value_t_or_exit!(matches.value_of("length"), u32); /// let also_len = value_t_or_exit!(matches, "length", u32); /// -/// println!("{} + 2: {}", len, len + 2); +/// assert_eq!(10, len + 2); /// # } /// ``` /// [`std::str::FromStr`]: https://doc.rust-lang.org/std/str/trait.FromStr.html @@ -123,24 +122,20 @@ macro_rules! value_t_or_exit { /// /// # Examples /// -/// ```no_run +/// ``` /// # #[macro_use] /// # extern crate clap; /// # use clap::App; /// # fn main() { /// let matches = App::new("myapp") /// .arg_from_usage("[seq]... 'A sequence of pos whole nums, i.e. 20 45'") -/// .get_matches(); +/// .get_matches_from(vec!["myapp", "8", "2"]); /// /// let vals = values_t!(matches.values_of("seq"), u32).unwrap_or_else(|e| e.exit()); -/// for v in &vals { -/// println!("{} + 2: {}", v, v + 2); -/// } +/// assert_eq!(10, vals.iter().fold(0, |i, acc| i + acc)); /// /// let vals = values_t!(matches, "seq", u32).unwrap_or_else(|e| e.exit()); -/// for v in &vals { -/// println!("{} + 2: {}", v, v + 2); -/// } +/// assert_eq!(10, vals.iter().fold(0, |i, acc| i + acc)); /// # } /// ``` /// [`std::str::FromStr`]: https://doc.rust-lang.org/std/str/trait.FromStr.html @@ -183,25 +178,21 @@ macro_rules! values_t { /// /// # Examples /// -/// ```no_run +/// ``` /// # #[macro_use] /// # extern crate clap; /// # use clap::App; /// # fn main() { /// let matches = App::new("myapp") /// .arg_from_usage("[seq]... 'A sequence of pos whole nums, i.e. 20 45'") -/// .get_matches(); +/// .get_matches_from(vec!["myapp", "8", "2"]); /// /// let vals = values_t_or_exit!(matches.values_of("seq"), u32); -/// for v in &vals { -/// println!("{} + 2: {}", v, v + 2); -/// } +/// assert_eq!(10, vals.iter().fold(0, |i, acc| i + acc)); /// /// // type for example only /// let vals: Vec = values_t_or_exit!(matches, "seq", u32); -/// for v in &vals { -/// println!("{} + 2: {}", v, v + 2); -/// } +/// assert_eq!(10, vals.iter().fold(0, |i, acc| i + acc)); /// # } /// ``` /// [`values_t!(/* ... */).unwrap_or_else(|e| e.exit())`]: ./macro.values_t!.html @@ -266,9 +257,8 @@ macro_rules! _clap_count_exprs { /// /// # Examples /// -/// ```no_run -/// # #[macro_use] -/// # extern crate clap; +/// ``` +/// # #[macro_use] extern crate clap; /// # use clap::{App, Arg}; /// arg_enum!{ /// #[derive(Debug)] @@ -283,7 +273,7 @@ macro_rules! _clap_count_exprs { /// fn main() { /// let m = App::new("app") /// .arg_from_usage(" 'the foo'") -/// .get_matches(); +/// .get_matches_from(vec!["app", "bar"]); /// let f = value_t!(m, "foo", Foo).unwrap_or_else(|e| e.exit()); /// /// // Use f like any other Foo variant... @@ -371,9 +361,8 @@ macro_rules! arg_enum { /// /// # Examples /// -/// ```no_run -/// # #[macro_use] -/// # extern crate clap; +/// ``` +/// # #[macro_use] extern crate clap; /// # use clap::App; /// # fn main() { /// let m = App::new("app") @@ -395,14 +384,12 @@ macro_rules! crate_version { /// /// # Examples /// -/// ```no_run -/// # #[macro_use] -/// # extern crate clap; +/// ``` +/// # #[macro_use] extern crate clap; /// # use clap::App; /// # fn main() { -/// let m = App::new("app") -/// .author(crate_authors!()) -/// .get_matches(); +/// App::new("app") +/// .author(crate_authors!()); /// # } /// ``` #[cfg_attr(feature = "unstable", macro_export)] @@ -577,8 +564,7 @@ macro_rules! clap_app { /// literally) /// /// ```rust -/// # #[macro_use] -/// # extern crate clap; +/// # #[macro_use] extern crate clap; /// # use clap::{App, SubCommand}; /// // Note lowercase variants, the subcommand will be exactly as typed here /// subcommands!{ @@ -785,7 +771,7 @@ macro_rules! subcommands { fn from_os_str(s: &::std::ffi::OsStr) -> Self { use ::clap::OsStrExt; match &s.to_string_lossy()[..] { - $($s => $e::$v),+, + $($v => $e::$v),+, _ => $e::External((*s)._split(b' ').map(ToOwned::to_owned).collect::>()), } } @@ -805,7 +791,7 @@ macro_rules! subcommands { fn into(self) -> &'static str { match self { $e::External(_) => "External", - $($e::$v => $s,)+ + $($e::$v => stringify!($v),)+ } } } @@ -837,7 +823,7 @@ macro_rules! subcommands { impl<'a> ::clap::SubCommandKey for $e { fn from_os_str(s: &::std::ffi::OsStr) -> Self { match &s.to_string_lossy()[..] { - $($s => $e::$v),+, + $(stringify!($v) => $e::$v),+, _ => unreachable!(), } } @@ -855,7 +841,7 @@ macro_rules! subcommands { impl<'a> ::std::convert::Into<&'static str> for $e { fn into(self) -> &'static str { match self { - $($e::$v => $s,)+ + $($e::$v => stringify!($v),)+ } } } From b5245477db7b099fe142c6dda9b81516534dd148 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Fri, 20 May 2016 20:38:47 -0400 Subject: [PATCH 06/18] feat: adds macros for using enums with args --- src/args/arg.rs | 7 +++++ src/macros.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/args/arg.rs b/src/args/arg.rs index cb0e74faa3c..c0a04194a77 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -369,6 +369,13 @@ impl<'a, 'b> Arg<'a, 'b> { parser.parse() } + /// FIXME: add docs + pub fn usage(self, u: &'a str) -> Self { + let mut new = Arg::from_usage(u); + new.name = self.name; + new + } + /// Sets the short version of the argument without the preceding `-`. /// /// By default `clap` automatically assigns `V` and `h` to the auto-generated `version` and diff --git a/src/macros.rs b/src/macros.rs index 1e41bde27f5..e45ead67ed1 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -284,7 +284,7 @@ macro_rules! _clap_count_exprs { /// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html /// [`std::fmt::Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html #[macro_export] -macro_rules! arg_enum { +macro_rules! arg_values { (@as_item $($i:item)*) => ($($i)*); (@impls ( $($tts:tt)* ) -> ($e:ident, $($v:ident),+)) => { arg_enum!(@as_item @@ -991,6 +991,76 @@ macro_rules! subcommands { }; } +/// FIXME: add docs +#[macro_export] +macro_rules! args { + (@as_item $($i:item)*) => ($($i)*); + (@impls ( $($tts:tt)* ) -> ($e:ident, $($v:ident),+)) => { + args!(@as_item + $($tts)* + + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + $($e::$v => write!(f, stringify!($v)),)+ + } + } + } + impl<'a> ::std::convert::Into<&'static str> for $e { + fn into(self) -> &'static str { + match self { + $($e::$v => $s,)+ + } + } + } + impl ::std::convert::AsRef for $e { + fn as_ref(&self) -> &'static str { + match *self { + $($e::$v => stringify!($v),)+ + } + } + } + impl $e { + #[allow(dead_code)] + pub fn variants() -> [&'static str; _clap_count_exprs!($(stringify!($v)),+)] { + [ + $(stringify!($v),)+ + ] + } + }); + }; + (#[$($m:meta),+] pub enum $e:ident { $($v:ident),+ } ) => { + args!(@impls + (#[$($m),+] + pub enum $e { + $($v),+ + }) -> ($e, $($v),+) + ); + }; + (#[$($m:meta),+] enum $e:ident { $($v:ident),+ } ) => { + args!(@impls + (#[$($m),+] + enum $e { + $($v),+ + }) -> ($e, $($v),+) + ); + }; + (pub enum $e:ident { $($v:ident),+ } ) => { + args!(@impls + (pub enum $e { + $($v),+ + }) -> ($e, $($v),+) + ); + }; + (enum $e:ident { $($v:ident),+ } ) => { + args!(@impls + (enum $e { + $($v),+ + }) -> ($e, $($v),+) + ); + }; +} + macro_rules! impl_settings { ($n:ident, $($v:ident => $c:ident),+) => { pub fn set(&mut self, s: $n) { From 66093a9da718efd0ca79b60fa05676be4927a1ea Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sun, 22 May 2016 11:32:16 -0400 Subject: [PATCH 07/18] fix(Macros): fixes some v3 feature macro typos --- src/args/arg.rs | 9 +++++++-- src/macros.rs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/args/arg.rs b/src/args/arg.rs index c0a04194a77..bbb7a32c7c4 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -123,8 +123,9 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` /// [`Arg::takes_value(true)`]: ./struct.Arg.html#method.takes_value /// [`Arg`]: ./struct.Arg.html - pub fn with_name(n: &'a str) -> Self { - Arg { name: n, ..Default::default() } + pub fn with_name(n: T) -> Self + where T: Into<&'a str> { + Arg { name: n.into(), ..Default::default() } } /// Creates a new instance of [`Arg`] from a .yml (YAML) file. @@ -487,6 +488,8 @@ impl<'a, 'b> Arg<'a, 'b> { /// .get_matches_from(vec![ /// "shorttest", "--help" /// ]); + /// + /// // ... /// ``` /// /// The above example displays @@ -1453,6 +1456,8 @@ impl<'a, 'b> Arg<'a, 'b> { /// .get_matches_from(vec![ /// "shorttest", "--help" /// ]); + /// + /// // ... /// ``` /// /// The above example displays diff --git a/src/macros.rs b/src/macros.rs index e45ead67ed1..e5adef59b0a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1009,7 +1009,7 @@ macro_rules! args { impl<'a> ::std::convert::Into<&'static str> for $e { fn into(self) -> &'static str { match self { - $($e::$v => $s,)+ + $($e::$v => stringify!($v),)+ } } } From 1f076b4a99459cadbe707538ca7dc20ece780574 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sun, 22 May 2016 11:41:23 -0400 Subject: [PATCH 08/18] imp(Macros): allows using a single macro with multiple enums The v3 macros can now be used with multiple enums: ```rust args! { enum SubCmd1Args { One, Two, Three } enum SubCmd2Args { Four, Five, Six } } ``` --- src/macros.rs | 120 +++++++++++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index e5adef59b0a..1a63f849e43 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -861,133 +861,133 @@ macro_rules! subcommands { } }); }; - (#[$($m:meta),+] pub enum $e:ident { External, $($v:ident=>$s:expr),+ } ) => { - subcommands!(@impls_s_ext + ($(#[$($m:meta),+] pub enum $e:ident { External, $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s_ext (#[$($m),+] pub enum $e { $($v),+, External(Vec<::std::ffi::OsString>), }) -> ($e, $($v=>$s),+) - ); + );)+ }; - (#[$($m:meta),+] enum $e:ident { External, $($v:ident=>$s:expr),+ } ) => { - subcommands!(@impls_s_ext + ($(#[$($m:meta),+] enum $e:ident { External, $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s_ext (#[$($m),+] enum $e { $($v),+, External(Vec<::std::ffi::OsString>), }) -> ($e, $($v=>$s:expr),+) - ); + );)+ }; - (#[$($m:meta),+] pub enum $e:ident { External, $($v:ident),+ } ) => { - subcommands!(@impls_ext + ($(#[$($m:meta),+] pub enum $e:ident { External, $($v:ident),+ })+ ) => { + $(subcommands!(@impls_ext (#[$($m),+] pub enum $e { $($v),+, External(Vec<::std::ffi::OsString>), }) -> ($e, $($v),+) - ); + );)+ }; - (#[$($m:meta),+] enum $e:ident { External, $($v:ident),+ } ) => { - subcommands!(@impls_ext + ($(#[$($m:meta),+] enum $e:ident { External, $($v:ident),+ })+ ) => { + $(subcommands!(@impls_ext (#[$($m),+] enum $e { $($v),+, External(Vec<::std::ffi::OsString>), }) -> ($e, $($v),+) - ); + );)+ }; - (#[$($m:meta),+] pub enum $e:ident { $($v:ident=>$s:expr),+ } ) => { - subcommands!(@impls_s + ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s (#[$($m),+] pub enum $e { $($v),+, }) -> ($e, $($v=>$s),+) - ); + );)+ }; - (#[$($m:meta),+] enum $e:ident { $($v:ident=>$s:expr),+ } ) => { - subcommands!(@impls_s + ($(#[$($m:meta),+] enum $e:ident { $($v:ident=>$s:expr),+ }) ) => { + $(subcommands!(@impls_s (#[$($m),+] enum $e { $($v),+, }) -> ($e, $($v=>$s:expr),+) - ); + );)+ }; - (#[$($m:meta),+] pub enum $e:ident { $($v:ident),+ } ) => { - subcommands!(@impls + ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident),+ })+ ) => { + $(subcommands!(@impls (#[$($m),+] pub enum $e { $($v),+, }) -> ($e, $($v),+) - ); + );)+ }; - (#[$($m:meta),+] enum $e:ident { $($v:ident),+ } ) => { - subcommands!(@impls + ($(#[$($m:meta),+] enum $e:ident { $($v:ident),+ })+ ) => { + $(subcommands!(@impls (#[$($m),+] enum $e { $($v),+, }) -> ($e, $($v),+) - ); + );)+ }; - (pub enum $e:ident { External, $($v:ident=>$s:expr),+ } ) => { - subcommands!(@impls_s_ext + ($(pub enum $e:ident { External, $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s_ext (pub enum $e { $($v),+, External(Vec<::std::ffi::OsString>), }) -> ($e, $($v=>$s),+) - ); + );)+ }; - (pub enum $e:ident { External, $($v:ident),+ } ) => { - subcommands!(@impls_ext + ($(pub enum $e:ident { External, $($v:ident),+ })+ ) => { + $(subcommands!(@impls_ext (pub enum $e { $($v),+, External(Vec<::std::ffi::OsString>), }) -> ($e, $($v),+) - ); + );)+ }; - (pub enum $e:ident { $($v:ident=>$s:expr),+ } ) => { - subcommands!(@impls_s + ($(pub enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s (pub enum $e { $($v),+, }) -> ($e, $($v=>$s),+) - ); + );)+ }; - (pub enum $e:ident { $($v:ident),+ } ) => { - subcommands!(@impls + ($(pub enum $e:ident { $($v:ident),+ })+ ) => { + $(subcommands!(@impls (pub enum $e { $($v),+, }) -> ($e, $($v),+) - ); + );)+ }; - (enum $e:ident { External, $($v:ident=>$s:expr),+ } ) => { - subcommands!(@impls_s_ext + ($(enum $e:ident { External, $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s_ext (enum $e { $($v),+, External(Vec<::std::ffi::OsString>), }) -> ($e, $($v=>$s),+) - ); + );)+ }; - (enum $e:ident { External, $($v:ident),+ } ) => { - subcommands!(@impls_ext + ($(enum $e:ident { External, $($v:ident),+ }) ) => { + $(subcommands!(@impls_ext (enum $e { $($v),+, External(Vec<::std::ffi::OsString>), }) -> ($e, $($v),+) - ); + );) }; - (enum $e:ident { $($v:ident=>$s:expr),+ } ) => { - subcommands!(@impls_s + ($(enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s (enum $e { $($v),+, }) -> ($e, $($v=>$s),+) - ); + );)+ }; - (enum $e:ident { $($v:ident),+ } ) => { - subcommands!(@impls + ($(enum $e:ident { $($v:ident),+ })+ ) => { + $(subcommands!(@impls (enum $e { $($v),+, }) -> ($e, $($v),+) - ); + );)+ }; } @@ -1029,35 +1029,35 @@ macro_rules! args { } }); }; - (#[$($m:meta),+] pub enum $e:ident { $($v:ident),+ } ) => { - args!(@impls + ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident),+ })+ ) => { + $(args!(@impls (#[$($m),+] pub enum $e { $($v),+ }) -> ($e, $($v),+) - ); + );)+ }; - (#[$($m:meta),+] enum $e:ident { $($v:ident),+ } ) => { - args!(@impls + ($(#[$($m:meta),+] enum $e:ident { $($v:ident),+ })+ ) => { + $(args!(@impls (#[$($m),+] enum $e { $($v),+ }) -> ($e, $($v),+) - ); + );)+ }; - (pub enum $e:ident { $($v:ident),+ } ) => { - args!(@impls + ($(pub enum $e:ident { $($v:ident),+ })+ ) => { + $(args!(@impls (pub enum $e { $($v),+ }) -> ($e, $($v),+) - ); + );)+ }; - (enum $e:ident { $($v:ident),+ } ) => { - args!(@impls + ($(enum $e:ident { $($v:ident),+ })+ ) => { + $(args!(@impls (enum $e { $($v),+ }) -> ($e, $($v),+) - ); + );)+ }; } From c6452bb9cbdf6559531d3253cd8000fc96d1e7cb Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sun, 22 May 2016 11:41:23 -0400 Subject: [PATCH 09/18] imp(Macros): allows using a single macro with multiple enums The v3 macros can now be used with multiple enums: ```rust args! { enum SubCmd1Args { One, Two, Three } enum SubCmd2Args { Four, Five, Six } } ``` --- src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 1a63f849e43..a092037f915 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -905,7 +905,7 @@ macro_rules! subcommands { }) -> ($e, $($v=>$s),+) );)+ }; - ($(#[$($m:meta),+] enum $e:ident { $($v:ident=>$s:expr),+ }) ) => { + ($(#[$($m:meta),+] enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { $(subcommands!(@impls_s (#[$($m),+] enum $e { From 7af67d0611e94443f2261492a218949021aeecfc Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sun, 22 May 2016 11:59:04 -0400 Subject: [PATCH 10/18] tests: fixes failing v3 tests --- examples/13a_enum_values_automatic.rs | 10 ++--- src/app/settings.rs | 55 ++++++++++++++++++++++++--- src/args/arg_matches.rs | 16 ++++---- src/macros.rs | 16 ++++---- tests/tests.rs | 8 ++-- 5 files changed, 76 insertions(+), 29 deletions(-) diff --git a/examples/13a_enum_values_automatic.rs b/examples/13a_enum_values_automatic.rs index 13b8779ccf5..34527d47213 100644 --- a/examples/13a_enum_values_automatic.rs +++ b/examples/13a_enum_values_automatic.rs @@ -2,9 +2,9 @@ // trait which is very straight forward. There are three ways to do this, for simple enums // meaning those that don't require 'pub' or any '#[derive()]' directives you can use clas's // simple_enum! macro. For those that require 'pub' or any '#[derive()]'s you can use clap's -// arg_enum! macro. The third way is to implement std::str::FromStr manually. +// arg_values! macro. The third way is to implement std::str::FromStr manually. // -// In most circumstances using either simple_enum! or arg_enum! is fine. +// In most circumstances using either simple_enum! or arg_values! is fine. // // In the following example we will create two enums using macros, assign a positional argument // that accepts only one of those values, and use clap to parse the argument. @@ -15,10 +15,10 @@ extern crate clap; use clap::{App, Arg}; -// Using arg_enum! is more like traditional enum declarations +// Using arg_values! is more like traditional enum declarations // // **NOTE:** Only bare variants are supported -arg_enum!{ +arg_values!{ #[derive(Debug)] pub enum Oof { Rab, @@ -27,7 +27,7 @@ arg_enum!{ } } -arg_enum!{ +arg_values!{ #[derive(Debug)] enum Foo { Bar, diff --git a/src/app/settings.rs b/src/app/settings.rs index 9f53e1c6f18..fa1b0a27f38 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -329,22 +329,67 @@ pub enum AppSettings { /// appropriatly. /// /// # Examples + /// + /// Because of how subcommands are parsed, there are the two ways in which you can creat + /// subcommands, either by using [`&str`] slices, or by using the newer and far superior + /// [enum macros]. + /// + /// This first method shows using the enum macros. /// /// ```rust + /// # #[macro_use] + /// # extern crate clap; + /// # use std::ffi::OsString; /// # use clap::{App, AppSettings}; + /// # fn main() { + /// + /// // Here we will support two subcommands, "do-stuff" and an external subcommand + /// subcommands! { + /// enum MyProg { + /// External, + /// DoStuff => "do-stuff" + /// } + /// } + /// /// // Assume there is an external subcommand named "subcmd" /// let m = App::new("myprog") /// .setting(AppSettings::AllowExternalSubcommands) /// .get_matches_from(vec![ - /// "myprog", "subcmd", "--option", "value", "-fff", "--flag" + /// "myprog", "subcmd", "--option", "value" /// ]); /// /// // All trailing arguments will be stored under the subcommand's sub-matches using a value /// // of the runtime subcommand name (in this case "subcmd") /// match m.subcommand() { - /// (external, Some(ext_m)) => { - /// let ext_args: Vec<&str> = ext_m.values_of(external).unwrap().collect(); - /// assert_eq!(ext_args, ["--option", "value", "-fff", "--flag"]); + /// Some((MyProg::External(ref args), _)) => { + /// assert_eq!(args, + /// &[OsString::from("subcmd"), + /// OsString::from("--option"), + /// OsString::from("value") + /// ]); + /// }, + /// Some((MyProg::DoStuff, _)) => {}, // "do-stuff" used + /// None => {}, // No subcommand used + /// } + /// # } + /// ``` + /// + /// This second method shows using the older `&str` method + /// + /// ```rust + /// # use clap::{App, AppSettings}; + /// // Assume there is an external subcommand named "subcmd" + /// let m = App::new("myprog") + /// .setting(AppSettings::AllowExternalSubcommands) + /// .get_matches_from(vec![ + /// "myprog", "subcmd", "--option", "value" + /// ]); + /// + /// // The external subcommand and all trailing arguments will be stored as a &str + /// // notice that we must specify a type hint + /// match m.subcommand::<&str>() { + /// Some((args, _)) => { + /// assert_eq!(args, "subcmd --option value"); /// }, /// _ => {}, /// } @@ -354,7 +399,7 @@ pub enum AppSettings { /// [`ArgMatches`]: ./struct.ArgMatches.html AllowExternalSubcommands, /// Specifies that any invalid UTF-8 code points should be treated as an error and fail - /// with a [`ErrorKind::InvalidUtf8`] error. + /// with a `ErrorKind::InvalidUtf8` error. /// /// **NOTE:** This rule only applies to argument values. Things such as flags, options, and /// [`SubCommand`]s themselves only allow valid UTF-8 code points. diff --git a/src/args/arg_matches.rs b/src/args/arg_matches.rs index a0d6d09880a..3c474af64f4 100644 --- a/src/args/arg_matches.rs +++ b/src/args/arg_matches.rs @@ -485,9 +485,9 @@ impl<'a> ArgMatches<'a> { /// with pattern matching! /// /// Because of how subcommands are parsed, there are the two ways in which you can creat - /// subcommands, either by using [`&str`] slices, or by using the newer and far superior + /// subcommands, either by using [`&str`] slices, or by using the newer and far superior /// [enum macros]. - /// + /// /// This first method shows using the enum macros. /// /// ```rust @@ -495,7 +495,6 @@ impl<'a> ArgMatches<'a> { /// # #[macro_use] /// # extern crate clap; /// # use clap::{App, AppSettings}; - /// # fn main() { /// /// // Here we support two subcomamnds, "do-stuff" and some external command unknown at compile /// // time. @@ -508,6 +507,9 @@ impl<'a> ArgMatches<'a> { /// } /// } /// + /// # fn main() { + /// // inside main() + /// /// // Assume there is an external subcommand named "subcmd" /// let m = App::new("myprog") /// .setting(AppSettings::AllowExternalSubcommands) @@ -515,13 +517,13 @@ impl<'a> ArgMatches<'a> { /// "myprog", "subcmd", "--option", "value" /// ]); /// - /// // The external subcommand and all trailing arguments will be stored as an Vec + /// // The external subcommand and all trailing arguments will be stored as an Vec /// // inside the `External` variant created by the macro /// match m.subcommand() { /// Some((MyProg::External(ref args), _)) => { - /// assert_eq!(args, - /// &[OsString::from("subcmd"), - /// OsString::from("--option"), + /// assert_eq!(args, + /// &[OsString::from("subcmd"), + /// OsString::from("--option"), /// OsString::from("value") /// ]); /// }, diff --git a/src/macros.rs b/src/macros.rs index a092037f915..1a79529934d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -260,7 +260,7 @@ macro_rules! _clap_count_exprs { /// ``` /// # #[macro_use] extern crate clap; /// # use clap::{App, Arg}; -/// arg_enum!{ +/// arg_values!{ /// #[derive(Debug)] /// pub enum Foo { /// Bar, @@ -287,7 +287,7 @@ macro_rules! _clap_count_exprs { macro_rules! arg_values { (@as_item $($i:item)*) => ($($i)*); (@impls ( $($tts:tt)* ) -> ($e:ident, $($v:ident),+)) => { - arg_enum!(@as_item + arg_values!(@as_item $($tts)* impl ::std::str::FromStr for $e { @@ -325,7 +325,7 @@ macro_rules! arg_values { }); }; (#[$($m:meta),+] pub enum $e:ident { $($v:ident),+ } ) => { - arg_enum!(@impls + arg_values!(@impls (#[$($m),+] pub enum $e { $($v),+ @@ -333,7 +333,7 @@ macro_rules! arg_values { ); }; (#[$($m:meta),+] enum $e:ident { $($v:ident),+ } ) => { - arg_enum!(@impls + arg_values!(@impls (#[$($m),+] enum $e { $($v),+ @@ -341,14 +341,14 @@ macro_rules! arg_values { ); }; (pub enum $e:ident { $($v:ident),+ } ) => { - arg_enum!(@impls + arg_values!(@impls (pub enum $e { $($v),+ }) -> ($e, $($v),+) ); }; (enum $e:ident { $($v:ident),+ } ) => { - arg_enum!(@impls + arg_values!(@impls (enum $e { $($v),+ }) -> ($e, $($v),+) @@ -967,13 +967,13 @@ macro_rules! subcommands { }) -> ($e, $($v=>$s),+) );)+ }; - ($(enum $e:ident { External, $($v:ident),+ }) ) => { + ($(enum $e:ident { External, $($v:ident),+ })+ ) => { $(subcommands!(@impls_ext (enum $e { $($v),+, External(Vec<::std::ffi::OsString>), }) -> ($e, $($v),+) - );) + );)+ }; ($(enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { $(subcommands!(@impls_s diff --git a/tests/tests.rs b/tests/tests.rs index e7456793a29..88dbd51ff8b 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -87,27 +87,27 @@ positional present with value: value subcmd NOT present "; -arg_enum!{ +arg_values!{ #[derive(Debug)] enum Val1 { ValOne, ValTwo } } -arg_enum!{ +arg_values!{ #[derive(Debug)] pub enum Val2 { ValOne, ValTwo } } -arg_enum!{ +arg_values!{ enum Val3 { ValOne, ValTwo } } -arg_enum!{ +arg_values!{ pub enum Val4 { ValOne, ValTwo From 19b1da4282dc446a497086346d26385b4b4e9cb3 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sun, 22 May 2016 15:35:23 -0400 Subject: [PATCH 11/18] docs: adds docs for new Arg::usage method --- src/args/arg.rs | 52 ++++++++++++++++++++++++++++++++++++++++- src/args/arg_matches.rs | 4 ++-- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/args/arg.rs b/src/args/arg.rs index bbb7a32c7c4..987480c5a7b 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -370,7 +370,57 @@ impl<'a, 'b> Arg<'a, 'b> { parser.parse() } - /// FIXME: add docs + /// Allows setting various settings utiling the same method as [`Arg::from_usage`]. + /// + /// **Pro Tip:** This is useful when one would like to utilize the new enum macros + /// ([`subcommands!`] and [`args!`]) while still being less verbose. + /// + /// # Examples + /// + /// To set `short` use a single valid UTF-8 codepoint. If you supply a leading `-` such as `-c` + /// it will be stripped. + /// + /// ```rust + /// # #[macro_use] + /// # extern crate clap; + /// # use clap::{App, Arg}; + /// args! { + /// enum MyProgArgs { + /// Arg1, + /// arg2 + /// } + /// } + /// # fn main() { + /// // Inside main() + /// Arg::with_name(MyProgArgs::Arg1) + /// .usage("-a, --arg 'some option argument'") + /// # ; + /// # } + /// ``` + /// + /// ```rust + /// # #[macro_use] + /// # extern crate clap; + /// # use clap::{App, Arg}; + /// args! { + /// enum MyProgArgs { + /// Arg1, + /// arg2 + /// } + /// } + /// # fn main() { + /// // Inside main() + /// let m = App::new("myprog") + /// .arg(Arg::with_name(MyProgArgs::Arg1) + /// .usage("-a, --arg 'some option argument'")); + /// .get_matches_from(vec!["myprog", "-a", "val"]) + /// + /// assert_eq!(m.value_of(MyProgArgs::Arg1).unwrap(), "val"); + /// # } + /// ``` + /// [`Arg::from_usage`]: ./struct.Arg.html#method.from_usage + /// [`subcommands!`]: ./macro.subcommands!.html + /// [`args!`]: ./macro.args!.html pub fn usage(self, u: &'a str) -> Self { let mut new = Arg::from_usage(u); new.name = self.name; diff --git a/src/args/arg_matches.rs b/src/args/arg_matches.rs index 3c474af64f4..fe8a67ff447 100644 --- a/src/args/arg_matches.rs +++ b/src/args/arg_matches.rs @@ -454,8 +454,8 @@ impl<'a> ArgMatches<'a> { /// [`Subcommand`]: ./struct.SubCommand.html /// [`App`]: ./struct.App.html /// [`ArgMatches`]: ./struct.ArgMatches.html - pub fn subcommand_name(&self) -> Option<&str> { - self.subcommand.as_ref().map(|sc| &sc.name[..]) + pub fn subcommand_name(&self) -> Option where S: SubCommandKey { + self.subcommand.as_ref().map(|sc| S::from_os_str(&sc.name)) } /// This brings together [`ArgMatches::subcommand_matches`] and [`ArgMatches::subcommand_name`] From 4b68c2be77b8ea7110bf21b6a65682f67a21ddd6 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sun, 22 May 2016 15:41:14 -0400 Subject: [PATCH 12/18] feat(Macros): adds alternate form of args! macro too The args! macro now has the same alternate form as the subcommands! macro. ```rust args! { enum ProgArgs { Arg1 => "FILE", Arg2 => "Other" } } ``` --- src/macros.rs | 184 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 126 insertions(+), 58 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 1a79529934d..5538590c08f 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -995,71 +995,139 @@ macro_rules! subcommands { #[macro_export] macro_rules! args { (@as_item $($i:item)*) => ($($i)*); - (@impls ( $($tts:tt)* ) -> ($e:ident, $($v:ident),+)) => { + (@impls_s ( $($tts:tt)* ) -> ($e:ident, $($v:ident=>$s:expr),+)) => { args!(@as_item - $($tts)* - - impl ::std::fmt::Display for $e { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match *self { - $($e::$v => write!(f, stringify!($v)),)+ + #[allow(unused_imports)] + #[derive(PartialEq)] + #[allow(non_camel_case_types)] + $($tts)* + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + $($e::$v => write!(f, $s),)+ + } } } - } - impl<'a> ::std::convert::Into<&'static str> for $e { - fn into(self) -> &'static str { - match self { - $($e::$v => stringify!($v),)+ + impl ::std::convert::AsRef for $e { + fn as_ref(&self) -> &'static str { + match *self { + $($e::$v => $s,)+ + } } } - } - impl ::std::convert::AsRef for $e { - fn as_ref(&self) -> &'static str { - match *self { - $($e::$v => stringify!($v),)+ + impl<'a> ::std::convert::Into<&'static str> for $e { + fn into(self) -> &'static str { + match self { + $($e::$v => $s,)+ + } } } - } - impl $e { - #[allow(dead_code)] - pub fn variants() -> [&'static str; _clap_count_exprs!($(stringify!($v)),+)] { - [ - $(stringify!($v),)+ - ] - } - }); - }; - ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident),+ })+ ) => { - $(args!(@impls - (#[$($m),+] - pub enum $e { - $($v),+ - }) -> ($e, $($v),+) - );)+ - }; - ($(#[$($m:meta),+] enum $e:ident { $($v:ident),+ })+ ) => { - $(args!(@impls - (#[$($m),+] - enum $e { - $($v),+ - }) -> ($e, $($v),+) - );)+ - }; - ($(pub enum $e:ident { $($v:ident),+ })+ ) => { - $(args!(@impls - (pub enum $e { - $($v),+ - }) -> ($e, $($v),+) - );)+ - }; - ($(enum $e:ident { $($v:ident),+ })+ ) => { - $(args!(@impls - (enum $e { - $($v),+ - }) -> ($e, $($v),+) - );)+ - }; -} + impl $e { + #[allow(dead_code)] + pub fn variants() -> [&'static str; _clap_count_exprs!($(stringify!($v)),+)] { + [ + $(stringify!($s),)+ + ] + } + }); + }; + (@impls ( $($tts:tt)* ) -> ($e:ident, $($v:ident),+)) => { + args!(@as_item + #[allow(unused_imports)] + #[derive(PartialEq)] + #[allow(non_camel_case_types)] + $($tts)* + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + $($e::$v => write!(f, stringify!($v)),)+ + } + } + } + impl<'a> ::std::convert::Into<&'static str> for $e { + fn into(self) -> &'static str { + match self { + $($e::$v => stringify!($v),)+ + } + } + } + impl ::std::convert::AsRef for $e { + fn as_ref(&self) -> &'static str { + match *self { + $($e::$v => stringify!($v),)+ + } + } + } + impl $e { + #[allow(dead_code)] + pub fn variants() -> [&'static str; _clap_count_exprs!($(stringify!($v)),+)] { + [ + $(stringify!($v),)+ + ] + } + }); + }; + ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(args!(@impls_s + (#[$($m),+] + pub enum $e { + $($v),+, + }) -> ($e, $($v=>$s),+) + );)+ + }; + ($(#[$($m:meta),+] enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(args!(@impls_s + (#[$($m),+] + enum $e { + $($v),+, + }) -> ($e, $($v=>$s:expr),+) + );)+ + }; + ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident),+ })+ ) => { + $(args!(@impls + (#[$($m),+] + pub enum $e { + $($v),+, + }) -> ($e, $($v),+) + );)+ + }; + ($(#[$($m:meta),+] enum $e:ident { $($v:ident),+ })+ ) => { + $(args!(@impls + (#[$($m),+] + enum $e { + $($v),+, + }) -> ($e, $($v),+) + );)+ + }; + ($(pub enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(args!(@impls_s + (pub enum $e { + $($v),+, + }) -> ($e, $($v=>$s),+) + );)+ + }; + ($(pub enum $e:ident { $($v:ident),+ })+ ) => { + $(args!(@impls + (pub enum $e { + $($v),+, + }) -> ($e, $($v),+) + );)+ + }; + ($(enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(args!(@impls_s + (enum $e { + $($v),+, + }) -> ($e, $($v=>$s),+) + );)+ + }; + ($(enum $e:ident { $($v:ident),+ })+ ) => { + $(args!(@impls + (enum $e { + $($v),+, + }) -> ($e, $($v),+) + );)+ + }; + } macro_rules! impl_settings { ($n:ident, $($v:ident => $c:ident),+) => { From 3ab891be00ee603c9fccb81c1c0372716452baac Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sun, 22 May 2016 15:54:02 -0400 Subject: [PATCH 13/18] style: fixes style of macros --- src/macros.rs | 612 +++++++++++++++++++++++++------------------------- 1 file changed, 306 insertions(+), 306 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 5538590c08f..8a147703d77 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -711,7 +711,7 @@ macro_rules! subcommands { ] } }); - }; + }; (@impls_s ( $($tts:tt)* ) -> ($e:ident, $($v:ident=>$s:expr),+)) => { subcommands!(@as_item #[allow(unused_imports)] @@ -759,236 +759,236 @@ macro_rules! subcommands { ] } }); - }; - (@impls_ext ( $($tts:tt)* ) -> ($e:ident, $($v:ident),+)) => { - subcommands!(@as_item - #[allow(unused_imports)] - use ::clap::SubCommandKey; - #[derive(PartialEq)] - #[allow(non_camel_case_types)] - $($tts)* - impl<'a> ::clap::SubCommandKey for $e { - fn from_os_str(s: &::std::ffi::OsStr) -> Self { - use ::clap::OsStrExt; - match &s.to_string_lossy()[..] { - $($v => $e::$v),+, - _ => $e::External((*s)._split(b' ').map(ToOwned::to_owned).collect::>()), - } - } - fn external(args: Vec<::std::ffi::OsString>) -> Option { - Some($e::External(args)) + }; + (@impls_ext ( $($tts:tt)* ) -> ($e:ident, $($v:ident),+)) => { + subcommands!(@as_item + #[allow(unused_imports)] + use ::clap::SubCommandKey; + #[derive(PartialEq)] + #[allow(non_camel_case_types)] + $($tts)* + impl<'a> ::clap::SubCommandKey for $e { + fn from_os_str(s: &::std::ffi::OsStr) -> Self { + use ::clap::OsStrExt; + match &s.to_string_lossy()[..] { + $($v => $e::$v),+, + _ => $e::External((*s)._split(b' ').map(ToOwned::to_owned).collect::>()), } } - impl ::std::fmt::Display for $e { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match *self { - $e::External(ref args) => write!(f, "{}", args.iter().map(|a| a.to_string_lossy()).collect::>().join(" ")), - $($e::$v => write!(f, stringify!($v)),)+ - } - } + fn external(args: Vec<::std::ffi::OsString>) -> Option { + Some($e::External(args)) } - impl<'a> ::std::convert::Into<&'static str> for $e { - fn into(self) -> &'static str { - match self { - $e::External(_) => "External", - $($e::$v => stringify!($v),)+ - } + } + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + $e::External(ref args) => write!(f, "{}", args.iter().map(|a| a.to_string_lossy()).collect::>().join(" ")), + $($e::$v => write!(f, stringify!($v)),)+ } } - impl ::std::convert::AsRef for $e { - fn as_ref(&self) -> &'static str { - match *self { - $e::External(_) => "External", - $($e::$v => stringify!($v),)+ - } + } + impl<'a> ::std::convert::Into<&'static str> for $e { + fn into(self) -> &'static str { + match self { + $e::External(_) => "External", + $($e::$v => stringify!($v),)+ } } - impl $e { - #[allow(dead_code)] - pub fn variants() -> [&'static str; _clap_count_exprs!("External", $(stringify!($v)),+)] { - [ - "External", - $(stringify!($v),)+ - ] - } - }); - }; - (@impls ( $($tts:tt)* ) -> ($e:ident, $($v:ident),+)) => { - subcommands!(@as_item - #[allow(unused_imports)] - use ::clap::SubCommandKey; - #[derive(PartialEq)] - #[allow(non_camel_case_types)] - $($tts)* - impl<'a> ::clap::SubCommandKey for $e { - fn from_os_str(s: &::std::ffi::OsStr) -> Self { - match &s.to_string_lossy()[..] { - $(stringify!($v) => $e::$v),+, - _ => unreachable!(), - } - } - fn external(_: Vec<::std::ffi::OsString>) -> Option { - None + } + impl ::std::convert::AsRef for $e { + fn as_ref(&self) -> &'static str { + match *self { + $e::External(_) => "External", + $($e::$v => stringify!($v),)+ } } - impl ::std::fmt::Display for $e { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match *self { - $($e::$v => write!(f, stringify!($v)),)+ - } + } + impl $e { + #[allow(dead_code)] + pub fn variants() -> [&'static str; _clap_count_exprs!("External", $(stringify!($v)),+)] { + [ + "External", + $(stringify!($v),)+ + ] + } + }); + }; + (@impls ( $($tts:tt)* ) -> ($e:ident, $($v:ident),+)) => { + subcommands!(@as_item + #[allow(unused_imports)] + use ::clap::SubCommandKey; + #[derive(PartialEq)] + #[allow(non_camel_case_types)] + $($tts)* + impl<'a> ::clap::SubCommandKey for $e { + fn from_os_str(s: &::std::ffi::OsStr) -> Self { + match &s.to_string_lossy()[..] { + $(stringify!($v) => $e::$v),+, + _ => unreachable!(), } } - impl<'a> ::std::convert::Into<&'static str> for $e { - fn into(self) -> &'static str { - match self { - $($e::$v => stringify!($v),)+ - } + fn external(_: Vec<::std::ffi::OsString>) -> Option { + None + } + } + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + $($e::$v => write!(f, stringify!($v)),)+ } } - impl ::std::convert::AsRef for $e { - fn as_ref(&self) -> &'static str { - match *self { - $($e::$v => stringify!($v),)+ - } + } + impl<'a> ::std::convert::Into<&'static str> for $e { + fn into(self) -> &'static str { + match self { + $($e::$v => stringify!($v),)+ } } - impl $e { - #[allow(dead_code)] - pub fn variants() -> [&'static str; _clap_count_exprs!($(stringify!($v)),+)] { - [ - $(stringify!($v),)+ - ] + } + impl ::std::convert::AsRef for $e { + fn as_ref(&self) -> &'static str { + match *self { + $($e::$v => stringify!($v),)+ } - }); - }; - ($(#[$($m:meta),+] pub enum $e:ident { External, $($v:ident=>$s:expr),+ })+ ) => { - $(subcommands!(@impls_s_ext - (#[$($m),+] - pub enum $e { - $($v),+, - External(Vec<::std::ffi::OsString>), - }) -> ($e, $($v=>$s),+) - );)+ - }; - ($(#[$($m:meta),+] enum $e:ident { External, $($v:ident=>$s:expr),+ })+ ) => { - $(subcommands!(@impls_s_ext - (#[$($m),+] - enum $e { - $($v),+, - External(Vec<::std::ffi::OsString>), - }) -> ($e, $($v=>$s:expr),+) - );)+ - }; - ($(#[$($m:meta),+] pub enum $e:ident { External, $($v:ident),+ })+ ) => { - $(subcommands!(@impls_ext - (#[$($m),+] - pub enum $e { - $($v),+, - External(Vec<::std::ffi::OsString>), - }) -> ($e, $($v),+) - );)+ - }; - ($(#[$($m:meta),+] enum $e:ident { External, $($v:ident),+ })+ ) => { - $(subcommands!(@impls_ext - (#[$($m),+] - enum $e { - $($v),+, - External(Vec<::std::ffi::OsString>), - }) -> ($e, $($v),+) - );)+ - }; - ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { - $(subcommands!(@impls_s - (#[$($m),+] - pub enum $e { - $($v),+, - }) -> ($e, $($v=>$s),+) - );)+ - }; - ($(#[$($m:meta),+] enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { - $(subcommands!(@impls_s - (#[$($m),+] - enum $e { - $($v),+, - }) -> ($e, $($v=>$s:expr),+) - );)+ - }; - ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident),+ })+ ) => { - $(subcommands!(@impls - (#[$($m),+] - pub enum $e { - $($v),+, - }) -> ($e, $($v),+) - );)+ - }; - ($(#[$($m:meta),+] enum $e:ident { $($v:ident),+ })+ ) => { - $(subcommands!(@impls - (#[$($m),+] - enum $e { - $($v),+, - }) -> ($e, $($v),+) - );)+ - }; - ($(pub enum $e:ident { External, $($v:ident=>$s:expr),+ })+ ) => { - $(subcommands!(@impls_s_ext - (pub enum $e { - $($v),+, - External(Vec<::std::ffi::OsString>), - }) -> ($e, $($v=>$s),+) - );)+ - }; - ($(pub enum $e:ident { External, $($v:ident),+ })+ ) => { - $(subcommands!(@impls_ext - (pub enum $e { - $($v),+, - External(Vec<::std::ffi::OsString>), - }) -> ($e, $($v),+) - );)+ - }; - ($(pub enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { - $(subcommands!(@impls_s - (pub enum $e { - $($v),+, - }) -> ($e, $($v=>$s),+) - );)+ - }; - ($(pub enum $e:ident { $($v:ident),+ })+ ) => { - $(subcommands!(@impls - (pub enum $e { - $($v),+, - }) -> ($e, $($v),+) - );)+ - }; - ($(enum $e:ident { External, $($v:ident=>$s:expr),+ })+ ) => { - $(subcommands!(@impls_s_ext - (enum $e { - $($v),+, - External(Vec<::std::ffi::OsString>), - }) -> ($e, $($v=>$s),+) - );)+ - }; - ($(enum $e:ident { External, $($v:ident),+ })+ ) => { - $(subcommands!(@impls_ext - (enum $e { - $($v),+, - External(Vec<::std::ffi::OsString>), - }) -> ($e, $($v),+) - );)+ - }; - ($(enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { - $(subcommands!(@impls_s - (enum $e { - $($v),+, - }) -> ($e, $($v=>$s),+) - );)+ - }; - ($(enum $e:ident { $($v:ident),+ })+ ) => { - $(subcommands!(@impls - (enum $e { - $($v),+, - }) -> ($e, $($v),+) - );)+ - }; + } + } + impl $e { + #[allow(dead_code)] + pub fn variants() -> [&'static str; _clap_count_exprs!($(stringify!($v)),+)] { + [ + $(stringify!($v),)+ + ] + } + }); + }; + ($(#[$($m:meta),+] pub enum $e:ident { External, $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s_ext + (#[$($m),+] + pub enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v=>$s),+) + );)+ + }; + ($(#[$($m:meta),+] enum $e:ident { External, $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s_ext + (#[$($m),+] + enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v=>$s:expr),+) + );)+ + }; + ($(#[$($m:meta),+] pub enum $e:ident { External, $($v:ident),+ })+ ) => { + $(subcommands!(@impls_ext + (#[$($m),+] + pub enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v),+) + );)+ + }; + ($(#[$($m:meta),+] enum $e:ident { External, $($v:ident),+ })+ ) => { + $(subcommands!(@impls_ext + (#[$($m),+] + enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v),+) + );)+ + }; + ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s + (#[$($m),+] + pub enum $e { + $($v),+, + }) -> ($e, $($v=>$s),+) + );)+ + }; + ($(#[$($m:meta),+] enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s + (#[$($m),+] + enum $e { + $($v),+, + }) -> ($e, $($v=>$s:expr),+) + );)+ + }; + ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident),+ })+ ) => { + $(subcommands!(@impls + (#[$($m),+] + pub enum $e { + $($v),+, + }) -> ($e, $($v),+) + );)+ + }; + ($(#[$($m:meta),+] enum $e:ident { $($v:ident),+ })+ ) => { + $(subcommands!(@impls + (#[$($m),+] + enum $e { + $($v),+, + }) -> ($e, $($v),+) + );)+ + }; + ($(pub enum $e:ident { External, $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s_ext + (pub enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v=>$s),+) + );)+ + }; + ($(pub enum $e:ident { External, $($v:ident),+ })+ ) => { + $(subcommands!(@impls_ext + (pub enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v),+) + );)+ + }; + ($(pub enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s + (pub enum $e { + $($v),+, + }) -> ($e, $($v=>$s),+) + );)+ + }; + ($(pub enum $e:ident { $($v:ident),+ })+ ) => { + $(subcommands!(@impls + (pub enum $e { + $($v),+, + }) -> ($e, $($v),+) + );)+ + }; + ($(enum $e:ident { External, $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s_ext + (enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v=>$s),+) + );)+ + }; + ($(enum $e:ident { External, $($v:ident),+ })+ ) => { + $(subcommands!(@impls_ext + (enum $e { + $($v),+, + External(Vec<::std::ffi::OsString>), + }) -> ($e, $($v),+) + );)+ + }; + ($(enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(subcommands!(@impls_s + (enum $e { + $($v),+, + }) -> ($e, $($v=>$s),+) + );)+ + }; + ($(enum $e:ident { $($v:ident),+ })+ ) => { + $(subcommands!(@impls + (enum $e { + $($v),+, + }) -> ($e, $($v),+) + );)+ + }; } /// FIXME: add docs @@ -1030,103 +1030,103 @@ macro_rules! args { ] } }); - }; - (@impls ( $($tts:tt)* ) -> ($e:ident, $($v:ident),+)) => { - args!(@as_item - #[allow(unused_imports)] - #[derive(PartialEq)] - #[allow(non_camel_case_types)] - $($tts)* - impl ::std::fmt::Display for $e { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match *self { - $($e::$v => write!(f, stringify!($v)),)+ - } + }; + (@impls ( $($tts:tt)* ) -> ($e:ident, $($v:ident),+)) => { + args!(@as_item + #[allow(unused_imports)] + #[derive(PartialEq)] + #[allow(non_camel_case_types)] + $($tts)* + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + $($e::$v => write!(f, stringify!($v)),)+ } } - impl<'a> ::std::convert::Into<&'static str> for $e { - fn into(self) -> &'static str { - match self { - $($e::$v => stringify!($v),)+ - } + } + impl<'a> ::std::convert::Into<&'static str> for $e { + fn into(self) -> &'static str { + match self { + $($e::$v => stringify!($v),)+ } } - impl ::std::convert::AsRef for $e { - fn as_ref(&self) -> &'static str { - match *self { - $($e::$v => stringify!($v),)+ - } + } + impl ::std::convert::AsRef for $e { + fn as_ref(&self) -> &'static str { + match *self { + $($e::$v => stringify!($v),)+ } } - impl $e { - #[allow(dead_code)] - pub fn variants() -> [&'static str; _clap_count_exprs!($(stringify!($v)),+)] { - [ - $(stringify!($v),)+ - ] - } - }); - }; - ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { - $(args!(@impls_s - (#[$($m),+] - pub enum $e { - $($v),+, - }) -> ($e, $($v=>$s),+) - );)+ - }; - ($(#[$($m:meta),+] enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { - $(args!(@impls_s - (#[$($m),+] - enum $e { - $($v),+, - }) -> ($e, $($v=>$s:expr),+) - );)+ - }; - ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident),+ })+ ) => { - $(args!(@impls - (#[$($m),+] - pub enum $e { - $($v),+, - }) -> ($e, $($v),+) - );)+ - }; - ($(#[$($m:meta),+] enum $e:ident { $($v:ident),+ })+ ) => { - $(args!(@impls - (#[$($m),+] - enum $e { - $($v),+, - }) -> ($e, $($v),+) - );)+ - }; - ($(pub enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { - $(args!(@impls_s - (pub enum $e { - $($v),+, - }) -> ($e, $($v=>$s),+) - );)+ - }; - ($(pub enum $e:ident { $($v:ident),+ })+ ) => { - $(args!(@impls - (pub enum $e { - $($v),+, - }) -> ($e, $($v),+) - );)+ - }; - ($(enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { - $(args!(@impls_s - (enum $e { - $($v),+, - }) -> ($e, $($v=>$s),+) - );)+ - }; - ($(enum $e:ident { $($v:ident),+ })+ ) => { - $(args!(@impls - (enum $e { - $($v),+, - }) -> ($e, $($v),+) - );)+ - }; + } + impl $e { + #[allow(dead_code)] + pub fn variants() -> [&'static str; _clap_count_exprs!($(stringify!($v)),+)] { + [ + $(stringify!($v),)+ + ] + } + }); + }; + ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(args!(@impls_s + (#[$($m),+] + pub enum $e { + $($v),+, + }) -> ($e, $($v=>$s),+) + );)+ + }; + ($(#[$($m:meta),+] enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(args!(@impls_s + (#[$($m),+] + enum $e { + $($v),+, + }) -> ($e, $($v=>$s:expr),+) + );)+ + }; + ($(#[$($m:meta),+] pub enum $e:ident { $($v:ident),+ })+ ) => { + $(args!(@impls + (#[$($m),+] + pub enum $e { + $($v),+, + }) -> ($e, $($v),+) + );)+ + }; + ($(#[$($m:meta),+] enum $e:ident { $($v:ident),+ })+ ) => { + $(args!(@impls + (#[$($m),+] + enum $e { + $($v),+, + }) -> ($e, $($v),+) + );)+ + }; + ($(pub enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(args!(@impls_s + (pub enum $e { + $($v),+, + }) -> ($e, $($v=>$s),+) + );)+ + }; + ($(pub enum $e:ident { $($v:ident),+ })+ ) => { + $(args!(@impls + (pub enum $e { + $($v),+, + }) -> ($e, $($v),+) + );)+ + }; + ($(enum $e:ident { $($v:ident=>$s:expr),+ })+ ) => { + $(args!(@impls_s + (enum $e { + $($v),+, + }) -> ($e, $($v=>$s),+) + );)+ + }; + ($(enum $e:ident { $($v:ident),+ })+ ) => { + $(args!(@impls + (enum $e { + $($v),+, + }) -> ($e, $($v),+) + );)+ + }; } macro_rules! impl_settings { From d488911b30feee5e3b5933bd524909b36ff85696 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sun, 22 May 2016 16:44:50 -0400 Subject: [PATCH 14/18] docs: provides docs for args! macro and fixes some subcommands! docs --- src/macros.rs | 151 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 131 insertions(+), 20 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 8a147703d77..04d118e3eeb 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -523,39 +523,45 @@ macro_rules! clap_app { }}; } -/// A convienience macro for defining enums that can be used to access `SubCommand`s. By using this -/// macro, all traits are implemented automatically. The traits implemented are, `SubCommandKey` -/// (an internal trait one needn't worry about), `Display`, `Into<&'static str>` and `AsRef`. -/// This macro also implements a `variants()` function which returns an array of `&'static str`s -/// containing the variant names. +/// A convienience macro for defining enums that can be used to access [`SubCommand`]s. By using +/// this macro, all traits are implemented automatically. The traits implemented are, +/// [`SubCommandKey`], [`Display`], [`Into<&'static str>`] and [`AsRef`]. This macro also +/// implements a `variants()` function which returns an array of [`&'static str`]s containing the +/// variant names. /// /// There are two ways to use this macro, in an as-is scenario where the variants one defines are /// exaclty how the subcommands are displayed to the end user. There is also an alternative way /// where the actual display of the subcommands can be changed. Examples of both are bellow. /// -/// This allows rustc to do some checking for you, i.e. if you add another -/// subcommand later, but forget to check for it, rustc will complain about -/// NonExaustive matches. Likewise, if you make a simple spelling or typing -/// error. +/// This allows `rustc` to do some checking for you, i.e. if you add another subcommand later, but +/// forget to check for it, `rustc` will complain about NonExaustive matches. Likewise, if you make +/// a simple spelling or typing error. /// -/// **Pro Tip:** It's good practice to make the name of the enum the same as -/// the parent command, and the variants the names of the actual subcommands +/// Another benefit is when using something like [`racer`] to provide autocompeletion, you can pick +/// from list a actual arg variants, and not risk mis-typing an arg name. +/// +/// **Pro Tip:** It's good practice to make the name of the enum the same as the parent command, +/// and the variants the names of the actual subcommands +/// +/// **NOTE:** Multiple subcommand enums can be declared inside the same `subcommands!` macro block. +/// +/// **NOTE:** If one wishes to implement these traits manually, that's also possible. /// /// # External Subcommands /// /// If you wish to support external subcommands, there are two simple things one must do. First, /// when using the `subcommands!` macro, the **first** variant you name, **must** be `External`, /// this tells the macro to generate all the appropriate portions to support external subcommands. -/// Second, you must use the `AppSettings::AllowExternalSubcommands` setting. +/// Second, you must use the [`AppSettings::AllowExternalSubcommands`] setting. /// /// After doing these two things, if a possible external subcommand is recognized, `clap` will /// return the `External(Vec)` variant. The wrapped `Vec` contains the args that were /// passed to the possible external subcommand (including the subcommand itself). Thse are stored -/// as `OsString`s since it's possible contain invalid UTF-8 code points on some platforms. +/// as [`OsString`]s since it's possible contain invalid UTF-8 code points on some platforms. /// /// **Pro Tip**: If you wish to get `&str`s instead and you're *sure* they won't contain invalid /// UTF-8, or you don't wish to support invalid UTF-8, it's as simple as using the following -/// iterator chain on the returned `Vec`: `v.iter().map(|s| s.to_str().expect("Invalid +/// iterator chain on the returned [`Vec`]: `v.iter().map(|s| s.to_str().expect("Invalid /// UTF-8")).collect::>()` /// /// # Examples @@ -591,9 +597,9 @@ macro_rules! clap_app { /// } /// ``` /// -/// Next, if you wish to support subcommands with things like hyphen characters, or don't like having -/// non-camel-case types, a second version of the macro exists which allows specifying a literal subcommand -/// which gets associated with a enum variant. +/// Next, if you wish to support subcommands with things like hyphen characters, or don't like +/// having non-camel-case types, a second version of the macro exists which allows specifying a +/// literal subcommand which gets associated with a enum variant. /// /// ```rust /// # #[macro_use] @@ -623,8 +629,8 @@ macro_rules! clap_app { /// } /// ``` /// -/// Finally, if one wishes to support external subcommands, simply ensure the first variant is called -/// `External` and the appropriate `AppSettings` variant is used. +/// Finally, if one wishes to support external subcommands, simply ensure the first variant is +/// called `External` and the appropriate [`AppSettings`] variant is used. /// /// ```rust /// # #[macro_use] @@ -656,6 +662,17 @@ macro_rules! clap_app { /// } /// } /// ``` +/// [`SubCommand`]: ./struct.SubCommand.html +/// [`SubCommandKey`]: ./trait.SubCommandKey.html +/// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html +/// [`Into<&'static str>`]: https://doc.rust-lang.org/std/convert/trait.Into.html +/// [`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html +/// [`&'static str`]: https://doc.rust-lang.org/std/primitive.str.html +/// [`AppSettings::AllowExternalSubcommands`]: ./enum.AppSettings.html#variant.AllowExternalSubcommands +/// [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html +/// [`AppSettings`]: ./enum.AppSettings.html +/// [`OsString`]: https://doc.rust-lang.org/std/ffi/struct.OsString.html +/// [`racer`]: https://github.com/phildawes/racer #[macro_export] macro_rules! subcommands { (@as_item $($i:item)*) => ($($i)*); @@ -991,7 +1008,101 @@ macro_rules! subcommands { }; } -/// FIXME: add docs +/// A convienience macro for defining enums that can be used to access [`Arg`]s. By using this +/// macro, all traits are implemented automatically. The traits implemented are, [`Display`], +/// [`Into<&'static str>`] and [`AsRef`]. This macro also implements a `variants()` function +/// which returns an array of [`&'static str`]s containing the variant names. +/// +/// There are two ways to use this macro; the most simple is in an as-is scenario where the +/// variants one defines are exaclty how the args would be accessed via a `&str`. There is also an +/// alternative way where the actual display of the args can be changed. Examples of both +/// are bellow. The second method is most useful with positional arguments whos name is also +/// displayed during usage strings and error messages. +/// +/// The benefit of using these macros versus strings is it allows `rustc` to do some checking for +/// you, i.e. if you add another arg later or change it's name, but forget to check for it +/// properly, `rustc` will complain about non-existing variants. Likewise, if you make a simple +/// spelling or typing error. +/// +/// Another benefit is when using something like [`racer`] to provide autocompeletion, you can pick +/// from list a actual arg variants, and not risk mis-typing an arg name. +/// +/// **Pro Tip:** It's good practice to make the name of the enum the same as the parent command +/// that they belong to and append the word `Args` so that it doesn't collide with any subcommands, +/// and the variants the names of the actual args +/// +/// **NOTE:** Multiple arg enums can be declared inside the same `args!` macro block. +/// +/// **Pro Tip**: Using [`Arg::usage`] is a simply way to get the benefits of using enums and less +/// verbose usage string initiation. +/// +/// # Examples +/// +/// First, an example showing the most basic use of the macro. (i.e. enum variants are used +/// literally). +/// +/// ```rust +/// # #[macro_use] extern crate clap; +/// # use clap::{App, Arg}; +/// args!{ +/// enum MyProg { +/// Verbose, +/// Config +/// } +/// } +/// +/// fn main() { +/// let m = App::new("myprog") +/// .arg(Arg::with_name(MyProg::Verbose) +/// .short("v") +/// .help("print verbose output")) +/// .arg(Arg::with_name(MyProg::Config) +/// .long("config") +/// .value_name("FILE") +/// .help("use a custom config file")) +/// .get_matches_from(vec!["myprog", "-v", "--config", "opts.cfg"]); +/// +/// assert!(m.is_present(MyProg::Verbose)); +/// assert_eq!(m.value_of(MyProg::Config), Some("opts.cfg")); +/// } +/// ``` +/// +/// Next, if you wish to support args with things like hyphen characters in the name, or don't like +/// having non-camel-case types for positional arguments which usually use either all lower-case or +/// all upper-case characters, a second version of the macro exists which allows specifying a +/// literal args, which is effectively the [`Arg::value_name`] which gets associated with a enum +/// variant. +/// +/// The benefit of the example below, is primarily with positional argumetns whos name is displayed +/// to the users in usage strings and help messages. If one were to view the help message of the +/// program below, the `File` woud be displayed as ``. +/// +/// ```rust +/// # #[macro_use] +/// # extern crate clap; +/// # use clap::{App, Arg}; +/// args!{ +/// enum MyProg { +/// File => "FILE" +/// } +/// } +/// fn main() { +/// let m = App::new("myprog") +/// .subcommand(SubCommand::with_name(MyProg::File)) +/// .get_matches_from(vec!["myprog", "file.txt"]); +/// +/// assert!(m.is_present(MyProg::File)); +/// assert_eq!(m.value_of(MyProg::File), Some("file.txt")); +/// } +/// ``` +/// [`Arg`]: ./struct.Arg.html +/// [`Arg::usage`]: ./struct.Arg.html#method.usage +/// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html +/// [`Into<&'static str>`]: https://doc.rust-lang.org/std/convert/trait.Into.html +/// [`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html +/// [`&'static str`]: https://doc.rust-lang.org/std/primitive.str.html +/// [`racer`]: https://github.com/phildawes/racer +/// [`Arg::value_name`]: ./struct.Arg.html#method.value_name #[macro_export] macro_rules! args { (@as_item $($i:item)*) => ($($i)*); From 4b4e89a0ceca40b4bc57ebf29f764d94e91a9e2b Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sun, 22 May 2016 16:45:13 -0400 Subject: [PATCH 15/18] docs: fixes some doc tests for Arg::usage --- src/args/arg.rs | 6 +++--- src/macros.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/args/arg.rs b/src/args/arg.rs index 987480c5a7b..d05bdde6c41 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -412,10 +412,10 @@ impl<'a, 'b> Arg<'a, 'b> { /// // Inside main() /// let m = App::new("myprog") /// .arg(Arg::with_name(MyProgArgs::Arg1) - /// .usage("-a, --arg 'some option argument'")); - /// .get_matches_from(vec!["myprog", "-a", "val"]) + /// .usage("-a, --arg 'some option argument'")) + /// .get_matches_from(vec!["myprog", "-a", "val"]); /// - /// assert_eq!(m.value_of(MyProgArgs::Arg1).unwrap(), "val"); + /// assert_eq!(m.value_of(MyProgArgs::Arg1), Some("val")); /// # } /// ``` /// [`Arg::from_usage`]: ./struct.Arg.html#method.from_usage diff --git a/src/macros.rs b/src/macros.rs index 04d118e3eeb..5f0fa5ececb 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1088,7 +1088,7 @@ macro_rules! subcommands { /// } /// fn main() { /// let m = App::new("myprog") -/// .subcommand(SubCommand::with_name(MyProg::File)) +/// .arg(Arg::with_name(MyProg::File)) /// .get_matches_from(vec!["myprog", "file.txt"]); /// /// assert!(m.is_present(MyProg::File)); From be11ab5ade2905f1d70ee5658959d89b19dc74e0 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sun, 22 May 2016 17:13:55 -0400 Subject: [PATCH 16/18] tests: fixes failing settings test --- src/app/settings.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/app/settings.rs b/src/app/settings.rs index fa1b0a27f38..dd919149d46 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -329,11 +329,11 @@ pub enum AppSettings { /// appropriatly. /// /// # Examples - /// + /// /// Because of how subcommands are parsed, there are the two ways in which you can creat - /// subcommands, either by using [`&str`] slices, or by using the newer and far superior + /// subcommands, either by using [`&str`] slices, or by using the newer and far superior /// [enum macros]. - /// + /// /// This first method shows using the enum macros. /// /// ```rust @@ -341,7 +341,6 @@ pub enum AppSettings { /// # extern crate clap; /// # use std::ffi::OsString; /// # use clap::{App, AppSettings}; - /// # fn main() { /// /// // Here we will support two subcommands, "do-stuff" and an external subcommand /// subcommands! { @@ -351,6 +350,8 @@ pub enum AppSettings { /// } /// } /// + /// # fn main() { + /// // Inside main() /// // Assume there is an external subcommand named "subcmd" /// let m = App::new("myprog") /// .setting(AppSettings::AllowExternalSubcommands) @@ -362,9 +363,9 @@ pub enum AppSettings { /// // of the runtime subcommand name (in this case "subcmd") /// match m.subcommand() { /// Some((MyProg::External(ref args), _)) => { - /// assert_eq!(args, - /// &[OsString::from("subcmd"), - /// OsString::from("--option"), + /// assert_eq!(args, + /// &[OsString::from("subcmd"), + /// OsString::from("--option"), /// OsString::from("value") /// ]); /// }, From 0d5eecf65dbf4db68098d99e3f3a0c298d6c4377 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sun, 22 May 2016 21:54:00 -0400 Subject: [PATCH 17/18] examples: adds examples for args! macro --- examples/21a_enums_for_args.rs | 54 ++++++++++++++++++++++++++++++++++ examples/21b_enums_for_args.rs | 32 ++++++++++++++++++++ examples/21c_enums_for_args.rs | 44 +++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 examples/21a_enums_for_args.rs create mode 100644 examples/21b_enums_for_args.rs create mode 100644 examples/21c_enums_for_args.rs diff --git a/examples/21a_enums_for_args.rs b/examples/21a_enums_for_args.rs new file mode 100644 index 00000000000..9db767236c1 --- /dev/null +++ b/examples/21a_enums_for_args.rs @@ -0,0 +1,54 @@ +// There are multiple ways in which to use enums as the "Keys" to access args. This has the benefit +// of not being "stringly-typed" and is *far* less error prone. It also allows one to take full +// advantage of auto-completion with tools like Racer (https://github.com/phildawes/racer). It also +// lets rustc do the error checking for you when you decide to change arugment names and such. + +// This first method we'll look at is the most simple version, where enum variants are used +// literally as the arg keys. For args like positional arguments, this means one must either make +// the variants how they'd like them to be displayed in help messages and such. This can be at odds +// with Rust best practices for style guides, so in other examples we'll look at other ways use +// enums with Args. + +// On to the code... + +// First we import the clap macros +#[macro_use] +extern crate clap; +use clap::{App, Arg}; + +// Next we declare the "keys" we'll use to access the arguments +// +// NOTE: The enums support use of `pub` or `#[meta]` style attributes (like derive(), or cfg() +// attributes), or any combinations of those two elements +args!{ + enum MyProg { + Verbose, + Config + } +} + +fn main() { + // We declare the App struct like normal. We could also use a use statement such as + // `use MyProg::*;` to save a few key-strokes, but for this simple example we'll leave + // that out. + let m = App::new("myprog") + .arg(Arg::with_name(MyProg::Verbose) + .short("v") + .help("print verbose output")) + .arg(Arg::with_name(MyProg::Config) + .long("config") + .value_name("FILE") + .help("use a custom config file")) + .get_matches(); + + // Now to access the args we use the enum variants + if m.is_present(MyProg::Verbose) { + println!("Printing verbosely..."); + } + + //println!("Verbose used {} times", m.occurrences_of(MyProg::Verbos)) // ERROR typo! (Mising E) + + if let Some(file) = m.value_of(MyProg::Config) { + println!("Using config file: {}", file); + } +} diff --git a/examples/21b_enums_for_args.rs b/examples/21b_enums_for_args.rs new file mode 100644 index 00000000000..fdf82c60128 --- /dev/null +++ b/examples/21b_enums_for_args.rs @@ -0,0 +1,32 @@ +// This next method we'll look at an instance where we'll want the arg to be displayed differently +// from the enum variant itself + +// We import the clap macros +#[macro_use] +extern crate clap; +use clap::{App, Arg}; + +// Next we declare the "keys" we'll use to access the arguments associated a display version +// The downside to using this form is that it's all or othing, either all args use a "display" +// version or are used literally. The next example shows one way we can get around this... +args!{ + enum MyProg { + Config => "FILE" + } +} + +fn main() { + use MyProg::*; + let m = App::new("myprog") + .arg(Arg::with_name(Config) + .takes_value(true) + .required(true) + .help("The custom config file to use")) + .get_matches(); + + // Run this program with --help to see that `Config` appears as "FILE" + + if let Some(file) = m.value_of(Config) { + println!("Using config file: {}", file); + } +} diff --git a/examples/21c_enums_for_args.rs b/examples/21c_enums_for_args.rs new file mode 100644 index 00000000000..99f7218b7dd --- /dev/null +++ b/examples/21c_enums_for_args.rs @@ -0,0 +1,44 @@ +// In this method we go back to the original, but find a way to get the desired affect without +// having to give a "display" version for all args + +// We import the clap macros +#[macro_use] +extern crate clap; +use clap::{App, Arg, SubCommand}; + +// Everything starts out normal, we declare the enums just like before. +// +// Notice we can also put multiple enums into a single `args!` macro. This is useful if we're using +// subcommands. +args!{ + enum MyProgArgs { + Config + } + enum TestArgs { + Verbose, + SomeFlag + } +} + +fn main() { + use MyProgArgs::*; + use TestArgs::*; + + let m = App::new("myprog") + .arg(Arg::with_name(Config) + .usage(" 'The custom config file to use'")) // Here we use a usage method which + // allows setting all the normal + // usage style settings (Same as + // Arg::from_usage), but with the + // added benefit of "renaming" the + // display version of this arg. In + // this example, "FILE" + .subcommand(SubCommand::with_name("test") + .arg(Arg::with_name(Verbose) + .usage("-v, --verbose 'print verbose output'"))) + .arg(Arg::with_name(SomeFlag) + .usage("--some-flag 'some imaginary flag'"))) + .get_matches(); + + // more logic here... +} From 92fb7215b3308e6773c0469f439155c453364ffc Mon Sep 17 00:00:00 2001 From: Kevin K Date: Sun, 22 May 2016 22:30:40 -0400 Subject: [PATCH 18/18] examples: adds examples for subcommands! macro --- examples/22a_enums_for_subcommands.rs | 64 +++++++++++++++++++++++++++ examples/22b_enums_for_subcommands.rs | 42 ++++++++++++++++++ examples/22c_enums_for_subcommands.rs | 45 +++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 examples/22a_enums_for_subcommands.rs create mode 100644 examples/22b_enums_for_subcommands.rs create mode 100644 examples/22c_enums_for_subcommands.rs diff --git a/examples/22a_enums_for_subcommands.rs b/examples/22a_enums_for_subcommands.rs new file mode 100644 index 00000000000..56457f1ea8a --- /dev/null +++ b/examples/22a_enums_for_subcommands.rs @@ -0,0 +1,64 @@ +// There are multiple ways in which to use enums as the "Keys" to access subcommands. This has the +// benefit of not being "stringly-typed" and is *far* less error prone. It also allows one to take +// full advantage of auto-completion with tools like Racer (https://github.com/phildawes/racer). It +// also lets rustc do the error checking for you when you decide to change arugment names and such. +// Finally, but certainly not least, it allows rustc to check against all possible variants when +// pattern matching, and complain about NonExaustive matches when one adds a new subcommand but +// forgets to check for it! +// +// This first method we'll look at is the most simple version, where enum variants are used +// literally as the subcommand keys and displayed to the user as such. This can be at odds +// with Rust best practices for style guides, so in other examples we'll look at other ways use +// enums with subcommands. +// +// Pro Tip: It's a good idea to name your enum after the parent command that the subcommands belong +// to. This avoids massive collisions and typos, and allows pattern matching against variants. + +// On to the code... + +// First we import the clap macros +#[macro_use] +extern crate clap; +use clap::{App, SubCommand}; + +// Next we declare the "keys"/"subcommands" we'll use to access the arguments. Notice, we use +// lowercase, non-camell-case variants so that users don't have to type "Clone" to access this +// subcommand. We'll need to use the allow(non_camel_case_types) to silence the warning. In another +// example we'll see how to get arounds this, and keep the compiler happy. +// +// NOTE: The enums support use of `pub` or `#[meta]` style attributes (like derive(), allow(), or +// cfg() attributes), or any combinations of those two elements +// +// NOTE 2: Just like the args! macro, one can declare multiple enums inside the same args! block +subcommands!{ + #[allow(non_camel_case_types)] + enum Git { + clone, + push, + pull + } +} + +fn main() { + // We declare the App struct like normal. We could also use a use statement such as + // `use MyProg::*;` to save a few key-strokes, but for this simple example we'll leave + // that out. + let m = App::new("git") + .subcommand(SubCommand::with_name(Git::clone)) + .subcommand(SubCommand::with_name(Git::push)) + .subcommand(SubCommand::with_name(Git::pull)) + .get_matches(); + + // Now to access the subcommands we can pattern match against the variants + // + // Note, the tuple is (Variant, ArgMatches), but we're not using the ArgMatches here, so we + // just throw them away with _ + match m.subcommand() { + Some((Git::clone, _)) => println!("Clone was used"), + Some((Git::push, _)) => println!("Push was used"), + Some((Git::pull, _)) => println!("Pull was used"), // <-- commment out this line to see the + // compiler complain about NonExaustive + // matches. Yay for no more strings! :) + None => println!("No subcommand was used... :("), + } +} diff --git a/examples/22b_enums_for_subcommands.rs b/examples/22b_enums_for_subcommands.rs new file mode 100644 index 00000000000..59b4e269eea --- /dev/null +++ b/examples/22b_enums_for_subcommands.rs @@ -0,0 +1,42 @@ +// Next we'll look at how we can have sane subcommands, or ones with hyphens, and still make the +// compiler happy about camel_case_types + +// First we import the clap macros +#[macro_use] +extern crate clap; +use clap::{App, SubCommand}; + +// Now we declare the variants, and their associated display/usage version +subcommands!{ + enum Git { + Clone => "clone", + Push = "push", + Pull => "pull", + DoStuff => "do-stuff" + } +} + +fn main() { + use Git::*; + + let m = App::new("git") + .subcommand(SubCommand::with_name(Clone)) + .subcommand(SubCommand::with_name(Push)) + .subcommand(SubCommand::with_name(Pull)) + .subcommand(SubCommand::with_name(DoStuff)) + .get_matches(); + + // Now to access the subcommands we can pattern match against the variants + // + // Note, the tuple is (Variant, ArgMatches), but we're not using the ArgMatches here, so we + // just throw them away with _ + match m.subcommand() { + Some((Clone, _)) => println!("clone was used"), + Some((Push, _)) => println!("push was used"), + Some((Pull, _)) => println!("pull was used"), // <-- commment out this line to see the + // compiler complain about NonExaustive + // matches. Yay for no more strings! :) + Some((DoStuff, _)) => println!("do-stuff was used"), + None => println!("No subcommand was used... :("), + } +} diff --git a/examples/22c_enums_for_subcommands.rs b/examples/22c_enums_for_subcommands.rs new file mode 100644 index 00000000000..db5f021a99a --- /dev/null +++ b/examples/22c_enums_for_subcommands.rs @@ -0,0 +1,45 @@ +// Now we'll see how we can support external subcommands. These should be used with caution, as +// they silence certain circumstances that would otherwise be and error. These conditions must be +// checked manually. The specific cases are when someone uses a "potential" external subcommand, +// i.e. something that wasn't defined at compile time, but it turns out no external subcommand for +// that exists. One must then check for, and inform the user of the error manually. + +// First we import the clap macros +#[macro_use] +extern crate clap; +use clap::{App, SubCommand}; + +// The only thing we need to do differently from the previous two examples is make the **FIRST** +// variant "External", and clap will generate all the necessary code. +// +// Note: All the previous version are still supported, such as pub/#[meta] items, and the basic or +// this alternate version +subcommands!{ + enum Git { + External, + Clone => "clone", + Push => "push", + Pull => "pull" + } +} + +fn main() { + use Git::*; + + let m = App::new("git") + .subcommand(SubCommand::with_name(Clone)) + .subcommand(SubCommand::with_name(Push)) + .subcommand(SubCommand::with_name(Pull)) + .get_matches(); + + // Now to access the subcommands we can pattern match against the variants like normal. the + // difference is now clap has generated an `External(Vec)` variant that contains the + // args along with the subcommand that was used. + match m.subcommand() { + Some((Clone, _)) => println!("Clone was used"), + Some((Push, _)) => println!("Push was used"), + Some((Pull, _)) => println!("Pull was used"), + Some((External(ref args), _)) => println!("An external subcommand was used: {:?}", args), + None => println!("No subcommand was used... :("), + } +}