2017-09-14 00:22:37 +00:00
|
|
|
|
use std::ffi::OsString;
|
2016-04-17 19:38:37 +00:00
|
|
|
|
use std::fmt;
|
|
|
|
|
use std::num::ParseIntError;
|
|
|
|
|
|
2018-12-07 23:43:31 +00:00
|
|
|
|
use crate::options::{flags, HelpString, VersionString};
|
|
|
|
|
use crate::options::parser::{Arg, Flag, ParseError};
|
2017-06-23 21:50:29 +00:00
|
|
|
|
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
2020-10-10 18:49:46 +00:00
|
|
|
|
/// A **misfire** is a thing that can happen instead of listing files — a
|
2016-04-17 19:38:37 +00:00
|
|
|
|
/// catch-all for anything outside the program’s normal execution.
|
|
|
|
|
#[derive(PartialEq, Debug)]
|
|
|
|
|
pub enum Misfire {
|
|
|
|
|
|
2017-07-26 16:48:18 +00:00
|
|
|
|
/// The getopts crate didn’t like these Arguments.
|
|
|
|
|
InvalidOptions(ParseError),
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
2017-07-26 16:48:18 +00:00
|
|
|
|
/// The user supplied an illegal choice to an Argument.
|
2017-09-14 00:22:37 +00:00
|
|
|
|
BadArgument(&'static Arg, OsString),
|
2016-08-29 01:56:32 +00:00
|
|
|
|
|
2018-12-17 04:48:49 +00:00
|
|
|
|
/// The user supplied a set of options
|
|
|
|
|
Unsupported(String),
|
|
|
|
|
|
2016-04-17 19:38:37 +00:00
|
|
|
|
/// The user asked for help. This isn’t strictly an error, which is why
|
|
|
|
|
/// this enum isn’t named Error!
|
2017-06-23 21:50:29 +00:00
|
|
|
|
Help(HelpString),
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
|
|
|
|
/// The user wanted the version number.
|
2017-08-05 18:46:47 +00:00
|
|
|
|
Version(VersionString),
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
Be stricter in strict mode
Now the code actually starts to use the Strictness flag that was added in the earlier commit! Well, the *code* doesn’t, but the tests do: the macros that create the test cases now have a parameter for which tests they should run. It’s usually ‘Both’ for both strict mode and default mode, but can be specified to only run in one, for when the results differ (usually when options override one another)
The downside to strict mode is that, now, *any* call to `matches.has` or `matches.get` could fail, because an option could have been specified twice, and this is the place where those are checked for. This makes the code a little less ergonomic in places, but that’s what the ? operator is for. The only place this has really had an effect is in `Classify::deduce`, which used to just return a boolean but can now fail.
In order to more thoroughly test the mode, some of the older parts of the code can now act more strict. For example, `TerminalColours::deduce` will now use the last-given option rather than searching for “colours” before “colors”.
Help and Version continue doing their own thing.
2017-08-09 08:21:29 +00:00
|
|
|
|
/// An option was given twice or more in strict mode.
|
|
|
|
|
Duplicate(Flag, Flag),
|
|
|
|
|
|
2016-04-17 19:38:37 +00:00
|
|
|
|
/// Two options were given that conflict with one another.
|
2017-07-26 16:48:18 +00:00
|
|
|
|
Conflict(&'static Arg, &'static Arg),
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
|
|
|
|
/// An option was given that does nothing when another one either is or
|
2020-10-10 18:49:46 +00:00
|
|
|
|
/// isn’t present.
|
2017-07-26 16:48:18 +00:00
|
|
|
|
Useless(&'static Arg, bool, &'static Arg),
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
|
|
|
|
/// An option was given that does nothing when either of two other options
|
|
|
|
|
/// are not present.
|
2017-07-26 16:48:18 +00:00
|
|
|
|
Useless2(&'static Arg, &'static Arg, &'static Arg),
|
|
|
|
|
|
|
|
|
|
/// A very specific edge case where --tree can’t be used with --all twice.
|
|
|
|
|
TreeAllAll,
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
|
|
|
|
/// A numeric option was given that failed to be parsed as a number.
|
|
|
|
|
FailedParse(ParseIntError),
|
2016-10-30 14:43:33 +00:00
|
|
|
|
|
|
|
|
|
/// A glob ignore was given that failed to be parsed as a pattern.
|
|
|
|
|
FailedGlobPattern(String),
|
2016-04-17 19:38:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Misfire {
|
|
|
|
|
|
|
|
|
|
/// The OS return code this misfire should signify.
|
2017-06-23 21:12:21 +00:00
|
|
|
|
pub fn is_error(&self) -> bool {
|
2020-10-10 14:57:40 +00:00
|
|
|
|
! matches!(self, Self::Help(_) | Self::Version(_))
|
2016-04-17 19:38:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-30 14:43:33 +00:00
|
|
|
|
impl From<glob::PatternError> for Misfire {
|
2020-10-10 12:55:26 +00:00
|
|
|
|
fn from(error: glob::PatternError) -> Self {
|
|
|
|
|
Self::FailedGlobPattern(error.to_string())
|
2016-10-30 14:43:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-17 19:38:37 +00:00
|
|
|
|
impl fmt::Display for Misfire {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2018-12-07 23:43:31 +00:00
|
|
|
|
use crate::options::parser::TakesValue;
|
2016-04-17 19:38:37 +00:00
|
|
|
|
|
2020-10-10 14:30:19 +00:00
|
|
|
|
match self {
|
|
|
|
|
Self::BadArgument(arg, attempt) => {
|
2017-09-14 00:22:37 +00:00
|
|
|
|
if let TakesValue::Necessary(Some(values)) = arg.takes_value {
|
|
|
|
|
write!(f, "Option {} has no {:?} setting ({})", arg, attempt, Choices(values))
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
write!(f, "Option {} has no {:?} setting", arg, attempt)
|
|
|
|
|
}
|
|
|
|
|
},
|
2020-10-10 18:49:46 +00:00
|
|
|
|
Self::InvalidOptions(e) => write!(f, "{}", e),
|
|
|
|
|
Self::Unsupported(e) => write!(f, "{}", e),
|
|
|
|
|
Self::Help(text) => write!(f, "{}", text),
|
|
|
|
|
Self::Version(version) => write!(f, "{}", version),
|
|
|
|
|
Self::Conflict(a, b) => write!(f, "Option {} conflicts with option {}", a, b),
|
|
|
|
|
Self::Duplicate(a, b) if a == b => write!(f, "Flag {} was given twice", a),
|
|
|
|
|
Self::Duplicate(a, b) => write!(f, "Flag {} conflicts with flag {}", a, b),
|
|
|
|
|
Self::Useless(a, false, b) => write!(f, "Option {} is useless without option {}", a, b),
|
|
|
|
|
Self::Useless(a, true, b) => write!(f, "Option {} is useless given option {}", a, b),
|
|
|
|
|
Self::Useless2(a, b1, b2) => write!(f, "Option {} is useless without options {} or {}", a, b1, b2),
|
|
|
|
|
Self::TreeAllAll => write!(f, "Option --tree is useless given --all --all"),
|
|
|
|
|
Self::FailedParse(ref e) => write!(f, "Failed to parse number: {}", e),
|
|
|
|
|
Self::FailedGlobPattern(ref e) => write!(f, "Failed to parse glob pattern: {}", e),
|
2016-04-17 19:38:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-08-10 22:34:39 +00:00
|
|
|
|
|
|
|
|
|
impl fmt::Display for ParseError {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2020-10-10 14:30:19 +00:00
|
|
|
|
match self {
|
|
|
|
|
Self::NeedsValue { flag, values: None } => write!(f, "Flag {} needs a value", flag),
|
|
|
|
|
Self::NeedsValue { flag, values: Some(cs) } => write!(f, "Flag {} needs a value ({})", flag, Choices(cs)),
|
|
|
|
|
Self::ForbiddenValue { flag } => write!(f, "Flag {} cannot take a value", flag),
|
|
|
|
|
Self::UnknownShortArgument { attempt } => write!(f, "Unknown argument -{}", *attempt as char),
|
|
|
|
|
Self::UnknownArgument { attempt } => write!(f, "Unknown argument --{}", attempt.to_string_lossy()),
|
2017-08-10 22:34:39 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-13 22:47:19 +00:00
|
|
|
|
|
|
|
|
|
impl Misfire {
|
2017-09-14 08:18:17 +00:00
|
|
|
|
/// Try to second-guess what the user was trying to do, depending on what
|
|
|
|
|
/// went wrong.
|
2017-09-13 22:47:19 +00:00
|
|
|
|
pub fn suggestion(&self) -> Option<&'static str> {
|
2017-09-14 08:18:17 +00:00
|
|
|
|
// ‘ls -lt’ and ‘ls -ltr’ are common combinations
|
2020-10-10 14:30:19 +00:00
|
|
|
|
match self {
|
|
|
|
|
Self::BadArgument(time, r) if *time == &flags::TIME && r == "r" => {
|
|
|
|
|
Some("To sort oldest files last, try \"--sort oldest\", or just \"-sold\"")
|
|
|
|
|
}
|
|
|
|
|
Self::InvalidOptions(ParseError::NeedsValue { ref flag, .. }) if *flag == Flag::Short(b't') => {
|
|
|
|
|
Some("To sort newest files last, try \"--sort newest\", or just \"-snew\"")
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
None
|
|
|
|
|
}
|
2017-09-13 22:47:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-13 22:49:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// A list of legal choices for an argument-taking option.
|
|
|
|
|
#[derive(PartialEq, Debug)]
|
|
|
|
|
pub struct Choices(&'static [&'static str]);
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for Choices {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(f, "choices: {}", self.0.join(", "))
|
|
|
|
|
}
|
|
|
|
|
}
|