summaryrefslogtreecommitdiffstats
path: root/src/options/misfire.rs
blob: d93abacba92191e50d2434a2d41d5f6a1d158f78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use std::ffi::OsString;
use std::fmt;
use std::num::ParseIntError;

use crate::options::{flags, HelpString, VersionString};
use crate::options::parser::{Arg, Flag, ParseError};


/// A **misfire** is a thing that can happen instead of listing files -- a
/// catch-all for anything outside the program’s normal execution.
#[derive(PartialEq, Debug)]
pub enum Misfire {

    /// The getopts crate didn’t like these Arguments.
    InvalidOptions(ParseError),

    /// The user supplied an illegal choice to an Argument.
    BadArgument(&'static Arg, OsString),

    /// The user supplied a set of options
    Unsupported(String),

    /// The user asked for help. This isn’t strictly an error, which is why
    /// this enum isn’t named Error!
    Help(HelpString),

    /// The user wanted the version number.
    Version(VersionString),

    /// An option was given twice or more in strict mode.
    Duplicate(Flag, Flag),

    /// Two options were given that conflict with one another.
    Conflict(&'static Arg, &'static Arg),

    /// An option was given that does nothing when another one either is or
    /// isn't present.
    Useless(&'static Arg, bool, &'static Arg),

    /// An option was given that does nothing when either of two other options
    /// are not present.
    Useless2(&'static Arg, &'static Arg, &'static Arg),

    /// A very specific edge case where --tree can’t be used with --all twice.
    TreeAllAll,

    /// A numeric option was given that failed to be parsed as a number.
    FailedParse(ParseIntError),

    /// A glob ignore was given that failed to be parsed as a pattern.
    FailedGlobPattern(String),
}

impl Misfire {

    /// The OS return code this misfire should signify.
    pub fn is_error(&self) -> bool {
        match self {
            Self::Help(_)     |
            Self::Version(_)  => false,
            _                 => true,
        }
    }
}

impl From<glob::PatternError> for Misfire {
    fn from(error: glob::PatternError) -> Self {
        Self::FailedGlobPattern(error.to_string())
    }
}

impl fmt::Display for Misfire {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use crate::options::parser::TakesValue;

        match self {
            Self::BadArgument(arg, attempt) => {
                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)
                }
            },
            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),
        }
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        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()),
        }
    }
}

impl Misfire {
    /// Try to second-guess what the user was trying to do, depending on what
    /// went wrong.
    pub fn suggestion(&self) -> Option<&'static str> {
        // ‘ls -lt’ and ‘ls -ltr’ are common combinations
        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
            }
        }
    }
}


/// 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(", "))
    }
}