From 934e6fbb643b2385efc23444fe6fce31494dc288 Mon Sep 17 00:00:00 2001 From: Alexander Kuvaev Date: Sun, 6 Sep 2015 00:17:32 +0300 Subject: [PATCH] feat(App): Added ability for users to handle errors themselves Now you can use get_matches_safe instead of get_mathces if you want to handle errors yourself. This will allow now to write false-negative tests and check what type of error occurs --- src/app/app.rs | 166 +++++++++++++++++++++++++++++++----------- src/app/errors.rs | 182 ++++++++++++++++++++++++++++++++++++++++++++-- src/app/mod.rs | 3 +- src/lib.rs | 2 +- 4 files changed, 305 insertions(+), 48 deletions(-) diff --git a/src/app/app.rs b/src/app/app.rs index 8c2ab47bd6e..0697b3dc8b4 100644 --- a/src/app/app.rs +++ b/src/app/app.rs @@ -1749,6 +1749,26 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.get_matches_from(env::args()) } + /// Starts the parsing process. Called on top level parent app **ONLY** then recursively calls + /// the real parsing function for all subcommands + /// + /// **NOTE:** This method should only be used when is absolutely necessary to handle errors manually. + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// let matches = App::new("myprog") + /// // Args and options go here... + /// .get_matches_safe() + /// .unwrap_or_else( |e| { panic!("An error occurs: {}", e) }); + /// ``` + pub fn get_matches_safe(self) -> Result, ClapError> { + // Start the parsing + self.get_matches_from_safe(env::args()) + } + /// Starts the parsing process. Called on top level parent app **ONLY** then recursively calls /// the real parsing function for all subcommands /// @@ -1807,6 +1827,66 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ matches } + /// Starts the parsing process. Called on top level parent app **ONLY** then recursively calls + /// the real parsing function for all subcommands + /// + /// **NOTE:** The first argument will be parsed as the binary name. + /// + /// **NOTE:** This method should only be used when absolutely necessary, such as needing to + /// parse arguments from something other than `std::env::args()`. If you are unsure, use + /// `App::get_matches_safe()` + /// + /// **NOTE:** This method should only be used when is absolutely necessary to handle errors manually. + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// let arg_vec = vec!["my_prog", "some", "args", "to", "parse"]; + /// + /// let matches = App::new("myprog") + /// // Args and options go here... + /// .get_matches_from_safe(arg_vec) + /// .unwrap_or_else( |e| { panic!("An error occurs: {}", e) }); + /// ``` + pub fn get_matches_from_safe(mut self, itr: I) + -> Result, ClapError> + where I: IntoIterator, + T: AsRef { + // Verify all positional assertions pass + self.verify_positionals(); + // If there are global arguments, we need to propgate them down to subcommands before + // parsing incase we run into a subcommand + self.propogate_globals(); + + let mut matches = ArgMatches::new(); + + let mut it = itr.into_iter(); + // Get the name of the program (argument 1 of env::args()) and determine the actual file + // that was used to execute the program. This is because a program called + // ./target/release/my_prog -a + // will have two arguments, './target/release/my_prog', '-a' but we don't want to display + // the full path when displaying help messages and such + if let Some(name) = it.next() { + let p = Path::new(name.as_ref()); + if let Some(f) = p.file_name() { + if let Ok(s) = f.to_os_string().into_string() { + if let None = self.bin_name { + self.bin_name = Some(s); + } + } + } + } + + // do the real parsing + if let Err(e) = self.get_matches_with(&mut matches, &mut it) { + return Err(e); + } + + Ok(matches) + } + fn verify_positionals(&mut self) { // Because you must wait until all arguments have been supplied, this is the first chance // to make assertions on positional argument indexes @@ -1883,7 +1963,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Warning(opt), format!("\n\t[valid values:{}]\n", valid_values), suffix.0), - ClapErrorType::Matches, + ClapErrorType::InvalidValue, App::get_args(matches)); } @@ -1939,7 +2019,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Warning(arg.as_ref()), Format::Warning(opt.to_string()), Format::Good(vals.len().to_string())), - ClapErrorType::Matches, + ClapErrorType::InvalidValue, App::get_args(matches)) ); } @@ -1953,7 +2033,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ arg_slice.is_empty() { return Err(self.report_error(format!("The argument '{}' does not allow empty \ values, but one was found.", Format::Warning(opt.to_string())), - ClapErrorType::Matches, + ClapErrorType::EmptyValue, App::get_args(matches))); } @@ -1972,7 +2052,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if let Some(ref vtor) = opt.validator { if let Err(e) = vtor(arg_slice.to_owned()) { return Err(self.report_error(e, - ClapErrorType::Opt, + ClapErrorType::OptionError, Some(vec![opt.name]))); } } @@ -2027,7 +2107,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ return Err(self.report_error( format!("The argument '{}' requires a value but none was supplied", Format::Warning(o.to_string())), - ClapErrorType::Matches, + ClapErrorType::EmptyValue, App::get_args(matches))); } } @@ -2076,7 +2156,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ self.bin_name.clone().unwrap_or(self.name.clone()), Format::Good("--"), arg_slice), - ClapErrorType::None, + ClapErrorType::InvalidSubcommand, None)); } } @@ -2088,7 +2168,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ format!("Found argument '{}', but {} wasn't expecting any", Format::Warning(arg.as_ref()), self.bin_name.clone().unwrap_or(self.name.clone())), - ClapErrorType::Matches, + ClapErrorType::UnexpectedArgument, App::get_args(matches))); } else if let Some(p) = self.positionals_idx.get(&pos_counter) { // Make sure this one doesn't conflict with anything @@ -2105,7 +2185,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ None => "one or more of the other specified \ arguments".to_owned() }), - ClapErrorType::Matches, + ClapErrorType::ArgumentConflict, App::get_args(matches))); } @@ -2128,7 +2208,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ but '{}' wasn't expecting any more values", Format::Warning(arg.as_ref()), Format::Warning(p.to_string())), - ClapErrorType::Matches, + ClapErrorType::TooMuchValues, App::get_args(matches))); } } @@ -2138,7 +2218,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ && arg_slice.is_empty() { return Err(self.report_error(format!("The argument '{}' does not allow empty \ values, but one was found.", Format::Warning(p.to_string())), - ClapErrorType::Matches, + ClapErrorType::EmptyValue, App::get_args(matches))); } // Check if it's already existing and update if so... @@ -2175,14 +2255,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if !p.empty_vals && arg_slice.is_empty() { return Err(self.report_error(format!("The argument '{}' does not allow empty \ values, but one was found.", Format::Warning(p.to_string())), - ClapErrorType::Matches, + ClapErrorType::EmptyValue, App::get_args(matches))); } if let Some(ref vtor) = p.validator { let f = &*vtor; if let Err(ref e) = f(arg_slice.to_owned()) { return Err(self.report_error(e.clone(), - ClapErrorType::Matches, + ClapErrorType::ValueError, App::get_args(matches))); } } @@ -2216,7 +2296,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ return Err(self.report_error(format!("The argument '{}' was found, but '{}' wasn't \ expecting any", Format::Warning(arg.as_ref()), self.bin_name.clone().unwrap_or(self.name.clone())), - ClapErrorType::Matches, + ClapErrorType::UnexpectedArgument, App::get_args(matches))); } } @@ -2232,7 +2312,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ return Err(self.report_error( format!("The argument '{}' requires a value but there wasn't any \ supplied", Format::Warning(o.to_string())), - ClapErrorType::Matches, + ClapErrorType::EmptyValue, App::get_args(matches))); } } @@ -2240,7 +2320,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ return Err(self.report_error( format!("The argument '{}' requires a value but none was supplied", Format::Warning(o.to_string())), - ClapErrorType::Matches, + ClapErrorType::EmptyValue, App::get_args(matches))); } else { @@ -2252,7 +2332,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ .iter() .fold(String::new(), |acc, s| acc + &format!("\n\t'{}'", Format::Error(s))[..])), - ClapErrorType::Matches, + ClapErrorType::MissingRequiredArgument, App::get_args(matches))); } } else { @@ -2260,7 +2340,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ format!("The argument '{}' requires a value but none was supplied", Format::Warning(format!("{}", self.positionals_idx.get( self.positionals_name.get(a).unwrap()).unwrap()))), - ClapErrorType::Matches, + ClapErrorType::EmptyValue, App::get_args(matches))); } } @@ -2323,7 +2403,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ let bn = self.bin_name.clone().unwrap_or(self.name.clone()); return Err(self.report_error(format!("'{}' requires a subcommand but none was provided", Format::Warning(&bn[..])), - ClapErrorType::Matches, + ClapErrorType::MissingSubcommand, App::get_args(matches))); } else if self.help_on_no_sc { self.print_help(); @@ -2338,7 +2418,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ .iter() .fold(String::new(), |acc, s| acc + &format!("\n\t'{}'", Format::Error(s))[..])), - ClapErrorType::Matches, + ClapErrorType::MissingRequiredArgument, App::get_args(matches))); } if matches.args.is_empty() && matches.subcommand_name().is_none() && self.help_on_no_args { @@ -2488,7 +2568,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ }); return Err(self.report_error(format!("The argument '{}' requires a value, but none was \ supplied", Format::Warning(format!("--{}", arg))), - ClapErrorType::Matches, + ClapErrorType::EmptyValue, App::get_args(matches))); } arg_val = Some(arg_vec[1]); @@ -2503,7 +2583,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ matches.args.remove(v.name); return Err(self.report_error(format!("The argument '{}' cannot be used with one or more of \ the other specified arguments", Format::Warning(format!("--{}", arg))), - ClapErrorType::Matches, + ClapErrorType::ArgumentConflict, App::get_args(matches))); } self.overrides.dedup(); @@ -2530,7 +2610,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ return Err(self.report_error(format!("The argument '{}' was supplied more than once, but \ does not support multiple values", Format::Warning(format!("--{}", arg))), - ClapErrorType::Matches, + ClapErrorType::UnexpectedMultipleUsage, App::get_args(matches))); } if let Some(av) = arg_val { @@ -2605,7 +2685,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Some(name) => format!("'{}'", Format::Warning(name)), None => "one or more of the specified arguments".to_owned() }), - ClapErrorType::Matches, + ClapErrorType::ArgumentConflict, App::get_args(matches))); } self.overrides.dedup(); @@ -2631,7 +2711,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if matches.args.contains_key(v.name) && !v.multiple { return Err(self.report_error(format!("The argument '{}' was supplied more than once, but does \ not support multiple values", Format::Warning(v.to_string())), - ClapErrorType::Matches, + ClapErrorType::UnexpectedMultipleUsage, App::get_args(matches))); } @@ -2713,7 +2793,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Err(self.report_error(format!("The argument '{}' isn't valid{}", Format::Warning(format!("--{}", arg)), suffix.0), - ClapErrorType::Matches, + ClapErrorType::InvalidArgument, App::get_args(matches))) } @@ -2726,13 +2806,13 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if !v.empty_vals && av.is_empty() && matches.args.contains_key(v.name) { return Err(self.report_error(format!("The argument '{}' does not allow empty \ values, but one was found.", Format::Warning(v.to_string())), - ClapErrorType::Matches, + ClapErrorType::EmptyValue, App::get_args(matches))); } if let Some(ref vtor) = v.validator { if let Err(e) = vtor(av.to_owned()) { return Err(self.report_error(e, - ClapErrorType::Matches, + ClapErrorType::ArgumentError, App::get_args(matches))); } } @@ -2751,7 +2831,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if !b { return Err(self.report_error(format!("The argument '{}' isn't valid", Format::Warning(format!("-{}", c))), - ClapErrorType::Matches, + ClapErrorType::InvalidArgument, App::get_args(matches))); } }, @@ -2791,7 +2871,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Some(name) => format!("'{}'", Format::Warning(name)), None => "one or more of the other specified arguments".to_owned() }), - ClapErrorType::Matches, + ClapErrorType::ArgumentConflict, App::get_args(matches))); } self.overrides.dedup(); @@ -2814,7 +2894,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ return Err(self.report_error(format!("The argument '{}' was supplied more than once, but \ does not support multiple values", Format::Warning(format!("-{}", arg))), - ClapErrorType::Matches, + ClapErrorType::UnexpectedMultipleUsage, App::get_args(matches))); } } else { @@ -2849,7 +2929,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ // Didn't match a flag or option, must be invalid Err(self.report_error(format!("The argument '{}' isn't valid", Format::Warning(format!("-{}", arg_c))), - ClapErrorType::Matches, + ClapErrorType::InvalidArgument, App::get_args(matches))) } @@ -2868,7 +2948,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ None => "with one or more of the other specified \ arguments".to_owned() }), - ClapErrorType::Matches, + ClapErrorType::ArgumentConflict, App::get_args(matches))); } self.overrides.dedup(); @@ -2895,7 +2975,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ return Err(self.report_error(format!("The argument '{}' was supplied more than once, but does \ not support multiple values", Format::Warning(format!("-{}", arg))), - ClapErrorType::Matches, + ClapErrorType::UnexpectedMultipleUsage, App::get_args(matches))); } @@ -2953,7 +3033,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Some(name) => format!("'{}'", Format::Warning(name)), None => "one or more of the other specified arguments".to_owned() }), - ClapErrorType::Matches, + ClapErrorType::ArgumentConflict, App::get_args(matches))); } else if self.groups.contains_key(name) { for n in self.get_group_members_names(name) { @@ -2973,7 +3053,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ None => format!("\"{}\"", Format::Warning(n)) } }), - ClapErrorType::Matches, + ClapErrorType::ArgumentConflict, App::get_args(matches))); } } @@ -3005,7 +3085,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if vals.len() == 1 || ( f.multiple && ( vals.len() % num as usize) == 1) {"as"}else{"ere"}), - ClapErrorType::Matches, + ClapErrorType::EmptyValue, App::get_args(matches))); } } @@ -3017,7 +3097,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Good(num.to_string()), Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), - ClapErrorType::Matches, + ClapErrorType::TooMuchValues, App::get_args(matches))); } } @@ -3029,7 +3109,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Good(num.to_string()), Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), - ClapErrorType::Matches, + ClapErrorType::TooFewValues, App::get_args(matches))); } } @@ -3043,7 +3123,11 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Good(num.to_string()), Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), - ClapErrorType::Matches, + if num > vals.len() as u8 { + ClapErrorType::TooMuchValues + } else { + ClapErrorType::TooFewValues + }, App::get_args(matches))); } } @@ -3055,7 +3139,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Good(num.to_string()), Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), - ClapErrorType::Matches, + ClapErrorType::TooMuchValues, App::get_args(matches))); } } @@ -3067,7 +3151,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Format::Good(num.to_string()), Format::Error(vals.len().to_string()), if vals.len() == 1 {"as"}else{"ere"}), - ClapErrorType::Matches, + ClapErrorType::TooFewValues, App::get_args(matches))); } } diff --git a/src/app/errors.rs b/src/app/errors.rs index 93135d85d94..8bfe9f79566 100644 --- a/src/app/errors.rs +++ b/src/app/errors.rs @@ -1,10 +1,182 @@ +use std::error::Error; +use std::fmt; + +/// Command line argument parser error types +#[derive(PartialEq, Debug)] pub enum ClapErrorType { - Matches, - Opt, - None + /// Error occurs when some possible values were set, but clap found unexpected value + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// # let result = App::new("myprog") + /// # .arg( + /// # Arg::with_name("debug").index(1) + /// .possible_value("fast") + /// .possible_value("slow") + /// # ).get_matches_from_safe(vec!["", "other"]); + InvalidValue, + /// Error occurs when clap found unexpected flag or option + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// # let result = App::new("myprog") + /// # .arg( + /// # Arg::from_usage("-f, --flag 'some flag'") + /// # ).get_matches_from_safe(vec!["", "--other"]); + InvalidArgument, + /// Error occurs when clap found unexpected subcommand + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg, SubCommand}; + /// # let result = App::new("myprog") + /// # .subcommand( + /// SubCommand::with_name("conifg") + /// .about("Used for configuration") + /// .arg(Arg::with_name("config_file") + /// .help("The configuration file to use") + /// .index(1)) + /// # ).get_matches_from_safe(vec!["", "other"]); + InvalidSubcommand, + /// Error occurs when option does not allow empty values but some was found + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// # let result = App::new("myprog") + /// # .arg( + /// # Arg::with_name("debug") + /// .empty_values(false)) + /// # .arg( + /// # Arg::with_name("color") + /// # ).get_matches_from_safe(vec!["", "--debug", "--color"]); + EmptyValue, + /// Parser inner error + OptionError, + /// Parser inner error + ArgumentError, + /// Parser inner error + ValueError, + /// Error occurs when argument got more values then were expected + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// # let result = App::new("myprog") + /// # .arg( + /// # Arg::with_name("debug").index(1) + /// .max_values(2) + /// # ).get_matches_from_safe(vec!["", "too", "much", "values"]); + TooMuchValues, + /// Error occurs when argument got less values then were expected + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// # let result = App::new("myprog") + /// # .arg( + /// # Arg::with_name("debug").index(1) + /// .min_values(3) + /// # ).get_matches_from_safe(vec!["", "too", "few"]); + TooFewValues, + /// Error occurs when clap find two ore more conflicting arguments + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// # let result = App::new("myprog") + /// # .arg( + /// # Arg::with_name("debug") + /// .conflicts_with("color") + /// # ).get_matches_from_safe(vec!["", "--debug", "--color"]); + ArgumentConflict, + /// Error occurs when one or more required arguments missing + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// # let result = App::new("myprog") + /// # .arg( + /// # Arg::with_name("debug") + /// .required(true) + /// # ).get_matches_from_safe(vec![""]); + MissingRequiredArgument, + /// Error occurs when required subcommand missing + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg, AppSettings, SubCommand}; + /// # let result = App::new("myprog") + /// # .setting(AppSettings::SubcommandRequired) + /// # .subcommand( + /// SubCommand::with_name("conifg") + /// .about("Used for configuration") + /// .arg(Arg::with_name("config_file") + /// .help("The configuration file to use") + /// .index(1)) + /// # ).get_matches_from_safe(vec![""]); + MissingSubcommand, + /// Error occurs when clap find argument while is was not expecting any + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App}; + /// # let result = App::new("myprog").get_matches_from_safe(vec!["", "--arg"]); + UnexpectedArgument, + /// Error occurs when argument was used multiple times and was not set as multiple. + /// + /// + /// # Example + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// # let result = App::new("myprog") + /// # .arg( + /// # Arg::with_name("debug") + /// .multiple(false) + /// # ).get_matches_from_safe(vec!["", "--debug", "--debug"]); + UnexpectedMultipleUsage } +/// Command line argument parser error +#[derive(Debug)] pub struct ClapError { - pub error: String, - pub error_type: ClapErrorType, + /// Formated error message + pub error: String, + /// Command line argument parser error type + pub error_type: ClapErrorType +} + +impl Error for ClapError { + fn description(&self) -> &str { + &*self.error + } +} + +impl fmt::Display for ClapError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.error) + } } \ No newline at end of file diff --git a/src/app/mod.rs b/src/app/mod.rs index 166db17370d..f71f25b7e50 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -4,4 +4,5 @@ mod suggestions; mod errors; pub use self::settings::AppSettings; -pub use self::app::App; \ No newline at end of file +pub use self::app::App; +pub use self::errors::{ClapError, ClapErrorType}; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 5511a9aee43..97dd24a8682 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ extern crate yaml_rust; #[cfg(feature = "yaml")] pub use yaml_rust::YamlLoader; pub use args::{Arg, SubCommand, ArgMatches, ArgGroup}; -pub use app::{App, AppSettings}; +pub use app::{App, AppSettings, ClapError, ClapErrorType}; pub use fmt::Format; #[macro_use]