|
|
@@ -43,6 +43,13 @@ pub type ShortArg = u8;
|
|
|
/// which flag it was.
|
|
|
pub type LongArg = &'static str;
|
|
|
|
|
|
+/// A **list of values** that an option can have, to be displayed when the
|
|
|
+/// user enters an invalid one or skips it.
|
|
|
+///
|
|
|
+/// This is literally just help text, and won’t be used to validate a value to
|
|
|
+/// see if it’s correct.
|
|
|
+pub type Values = &'static [&'static str];
|
|
|
+
|
|
|
/// A **flag** is either of the two argument types, because they have to
|
|
|
/// be in the same array together.
|
|
|
#[derive(PartialEq, Debug, Clone)]
|
|
|
@@ -88,13 +95,15 @@ pub enum Strictness {
|
|
|
pub enum TakesValue {
|
|
|
|
|
|
/// This flag has to be followed by a value.
|
|
|
- Necessary,
|
|
|
+ /// If there’s a fixed set of possible values, they can be printed out
|
|
|
+ /// with the error text.
|
|
|
+ Necessary(Option<Values>),
|
|
|
|
|
|
/// This flag will throw an error if there’s a value after it.
|
|
|
Forbidden,
|
|
|
|
|
|
/// This flag may be followed by a value to override its defaults
|
|
|
- Optional,
|
|
|
+ Optional(Option<Values>),
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -174,7 +183,7 @@ impl Args {
|
|
|
let arg = self.lookup_long(before)?;
|
|
|
let flag = Flag::Long(arg.long);
|
|
|
match arg.takes_value {
|
|
|
- Necessary|Optional => result_flags.push((flag, Some(after))),
|
|
|
+ Necessary(_)|Optional(_) => result_flags.push((flag, Some(after))),
|
|
|
Forbidden => return Err(ParseError::ForbiddenValue { flag })
|
|
|
}
|
|
|
}
|
|
|
@@ -185,16 +194,16 @@ impl Args {
|
|
|
let arg = self.lookup_long(long_arg_name)?;
|
|
|
let flag = Flag::Long(arg.long);
|
|
|
match arg.takes_value {
|
|
|
- Forbidden => result_flags.push((flag, None)),
|
|
|
- Necessary => {
|
|
|
+ Forbidden => result_flags.push((flag, None)),
|
|
|
+ Necessary(values) => {
|
|
|
if let Some(next_arg) = inputs.next() {
|
|
|
result_flags.push((flag, Some(next_arg)));
|
|
|
}
|
|
|
else {
|
|
|
- return Err(ParseError::NeedsValue { flag })
|
|
|
+ return Err(ParseError::NeedsValue { flag, values })
|
|
|
}
|
|
|
},
|
|
|
- Optional => {
|
|
|
+ Optional(_) => {
|
|
|
if let Some(next_arg) = inputs.next() {
|
|
|
result_flags.push((flag, Some(next_arg)));
|
|
|
}
|
|
|
@@ -221,7 +230,7 @@ impl Args {
|
|
|
// -abcdx= => error
|
|
|
//
|
|
|
// There’s no way to give two values in a cluster like this:
|
|
|
- // it's an error if any of the first set of arguments actually
|
|
|
+ // it’s an error if any of the first set of arguments actually
|
|
|
// takes a value.
|
|
|
if let Some((before, after)) = split_on_equals(short_arg) {
|
|
|
let (arg_with_value, other_args) = before.as_bytes().split_last().unwrap();
|
|
|
@@ -231,8 +240,8 @@ impl Args {
|
|
|
let arg = self.lookup_short(*byte)?;
|
|
|
let flag = Flag::Short(*byte);
|
|
|
match arg.takes_value {
|
|
|
- Forbidden|Optional => result_flags.push((flag, None)),
|
|
|
- Necessary => return Err(ParseError::NeedsValue { flag })
|
|
|
+ Forbidden|Optional(_) => result_flags.push((flag, None)),
|
|
|
+ Necessary(values) => return Err(ParseError::NeedsValue { flag, values })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -240,7 +249,7 @@ impl Args {
|
|
|
let arg = self.lookup_short(*arg_with_value)?;
|
|
|
let flag = Flag::Short(arg.short.unwrap());
|
|
|
match arg.takes_value {
|
|
|
- Necessary|Optional => result_flags.push((flag, Some(after))),
|
|
|
+ Necessary(_)|Optional(_) => result_flags.push((flag, Some(after))),
|
|
|
Forbidden => return Err(ParseError::ForbiddenValue { flag })
|
|
|
}
|
|
|
}
|
|
|
@@ -248,7 +257,7 @@ impl Args {
|
|
|
// If there’s no equals, then every character is parsed as
|
|
|
// its own short argument. However, if any of the arguments
|
|
|
// takes a value, then the *rest* of the string is used as
|
|
|
- // its value, and if there's no rest of the string, then it
|
|
|
+ // its value, and if there’s no rest of the string, then it
|
|
|
// uses the next one in the iterator.
|
|
|
//
|
|
|
// -a => ‘a’
|
|
|
@@ -262,8 +271,8 @@ impl Args {
|
|
|
let arg = self.lookup_short(*byte)?;
|
|
|
let flag = Flag::Short(*byte);
|
|
|
match arg.takes_value {
|
|
|
- Forbidden => result_flags.push((flag, None)),
|
|
|
- Necessary|Optional => {
|
|
|
+ Forbidden => result_flags.push((flag, None)),
|
|
|
+ Necessary(values)|Optional(values) => {
|
|
|
if index < bytes.len() - 1 {
|
|
|
let remnants = &bytes[index+1 ..];
|
|
|
result_flags.push((flag, Some(OsStr::from_bytes(remnants))));
|
|
|
@@ -275,10 +284,10 @@ impl Args {
|
|
|
else {
|
|
|
match arg.takes_value {
|
|
|
Forbidden => assert!(false),
|
|
|
- Necessary => {
|
|
|
- return Err(ParseError::NeedsValue { flag });
|
|
|
+ Necessary(_) => {
|
|
|
+ return Err(ParseError::NeedsValue { flag, values });
|
|
|
},
|
|
|
- Optional => {
|
|
|
+ Optional(_) => {
|
|
|
result_flags.push((flag, None));
|
|
|
}
|
|
|
}
|
|
|
@@ -386,7 +395,7 @@ impl<'a> MatchedFlags<'a> {
|
|
|
}
|
|
|
|
|
|
/// Returns the value of the argument that matches the predicate if it
|
|
|
- /// was specified, nothing if it wasn't, and an error in strict mode if
|
|
|
+ /// was specified, nothing if it wasn’t, and an error in strict mode if
|
|
|
/// multiple arguments matched the predicate.
|
|
|
///
|
|
|
/// It’s not possible to tell which flag the value belonged to from this.
|
|
|
@@ -427,15 +436,15 @@ impl<'a> MatchedFlags<'a> {
|
|
|
}
|
|
|
|
|
|
|
|
|
-/// A problem with the user's input that meant it couldn't be parsed into a
|
|
|
+/// A problem with the user’s input that meant it couldn’t be parsed into a
|
|
|
/// coherent list of arguments.
|
|
|
#[derive(PartialEq, Debug)]
|
|
|
pub enum ParseError {
|
|
|
|
|
|
/// A flag that has to take a value was not given one.
|
|
|
- NeedsValue { flag: Flag },
|
|
|
+ NeedsValue { flag: Flag, values: Option<Values> },
|
|
|
|
|
|
- /// A flag that can't take a value *was* given one.
|
|
|
+ /// A flag that can’t take a value *was* given one.
|
|
|
ForbiddenValue { flag: Flag },
|
|
|
|
|
|
/// A short argument, either alone or in a cluster, was not
|
|
|
@@ -563,10 +572,13 @@ mod parse_test {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+ const SUGGESTIONS: Values = &[ "example" ];
|
|
|
+
|
|
|
static TEST_ARGS: &[&Arg] = &[
|
|
|
&Arg { short: Some(b'l'), long: "long", takes_value: TakesValue::Forbidden },
|
|
|
&Arg { short: Some(b'v'), long: "verbose", takes_value: TakesValue::Forbidden },
|
|
|
- &Arg { short: Some(b'c'), long: "count", takes_value: TakesValue::Necessary }
|
|
|
+ &Arg { short: Some(b'c'), long: "count", takes_value: TakesValue::Necessary(None) },
|
|
|
+ &Arg { short: Some(b't'), long: "type", takes_value: TakesValue::Necessary(Some(SUGGESTIONS)) }
|
|
|
];
|
|
|
|
|
|
|
|
|
@@ -589,10 +601,15 @@ mod parse_test {
|
|
|
|
|
|
// Long args with values
|
|
|
test!(bad_equals: ["--long=equals"] => error ForbiddenValue { flag: Flag::Long("long") });
|
|
|
- test!(no_arg: ["--count"] => error NeedsValue { flag: Flag::Long("count") });
|
|
|
+ test!(no_arg: ["--count"] => error NeedsValue { flag: Flag::Long("count"), values: None });
|
|
|
test!(arg_equals: ["--count=4"] => frees: [], flags: [ (Flag::Long("count"), Some(OsStr::new("4"))) ]);
|
|
|
test!(arg_then: ["--count", "4"] => frees: [], flags: [ (Flag::Long("count"), Some(OsStr::new("4"))) ]);
|
|
|
|
|
|
+ // Long args with values and suggestions
|
|
|
+ test!(no_arg_s: ["--type"] => error NeedsValue { flag: Flag::Long("type"), values: Some(SUGGESTIONS) });
|
|
|
+ test!(arg_equals_s: ["--type=exa"] => frees: [], flags: [ (Flag::Long("type"), Some(OsStr::new("exa"))) ]);
|
|
|
+ test!(arg_then_s: ["--type", "exa"] => frees: [], flags: [ (Flag::Long("type"), Some(OsStr::new("exa"))) ]);
|
|
|
+
|
|
|
|
|
|
// Short args
|
|
|
test!(short: ["-l"] => frees: [], flags: [ (Flag::Short(b'l'), None) ]);
|
|
|
@@ -602,13 +619,19 @@ mod parse_test {
|
|
|
|
|
|
// Short args with values
|
|
|
test!(bad_short: ["-l=equals"] => error ForbiddenValue { flag: Flag::Short(b'l') });
|
|
|
- test!(short_none: ["-c"] => error NeedsValue { flag: Flag::Short(b'c') });
|
|
|
+ test!(short_none: ["-c"] => error NeedsValue { flag: Flag::Short(b'c'), values: None });
|
|
|
test!(short_arg_eq: ["-c=4"] => frees: [], flags: [(Flag::Short(b'c'), Some(OsStr::new("4"))) ]);
|
|
|
test!(short_arg_then: ["-c", "4"] => frees: [], flags: [(Flag::Short(b'c'), Some(OsStr::new("4"))) ]);
|
|
|
test!(short_two_together: ["-lctwo"] => frees: [], flags: [(Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(OsStr::new("two"))) ]);
|
|
|
test!(short_two_equals: ["-lc=two"] => frees: [], flags: [(Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(OsStr::new("two"))) ]);
|
|
|
test!(short_two_next: ["-lc", "two"] => frees: [], flags: [(Flag::Short(b'l'), None), (Flag::Short(b'c'), Some(OsStr::new("two"))) ]);
|
|
|
|
|
|
+ // Short args with values and suggestions
|
|
|
+ test!(short_none_s: ["-t"] => error NeedsValue { flag: Flag::Short(b't'), values: Some(SUGGESTIONS) });
|
|
|
+ test!(short_two_together_s: ["-texa"] => frees: [], flags: [(Flag::Short(b't'), Some(OsStr::new("exa"))) ]);
|
|
|
+ test!(short_two_equals_s: ["-t=exa"] => frees: [], flags: [(Flag::Short(b't'), Some(OsStr::new("exa"))) ]);
|
|
|
+ test!(short_two_next_s: ["-t", "exa"] => frees: [], flags: [(Flag::Short(b't'), Some(OsStr::new("exa"))) ]);
|
|
|
+
|
|
|
|
|
|
// Unknown args
|
|
|
test!(unknown_long: ["--quiet"] => error UnknownArgument { attempt: os("quiet") });
|
|
|
@@ -639,7 +662,7 @@ mod matches_test {
|
|
|
}
|
|
|
|
|
|
static VERBOSE: Arg = Arg { short: Some(b'v'), long: "verbose", takes_value: TakesValue::Forbidden };
|
|
|
- static COUNT: Arg = Arg { short: Some(b'c'), long: "count", takes_value: TakesValue::Necessary };
|
|
|
+ static COUNT: Arg = Arg { short: Some(b'c'), long: "count", takes_value: TakesValue::Necessary(None) };
|
|
|
|
|
|
|
|
|
test!(short_never: [], has VERBOSE => false);
|