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
use std::error::Error;
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};

/// An error for an invalid named argument.
#[derive(Debug)]
pub enum OptionError {
    /// The named argument is unrecognized.
    ///
    /// For example, the user passed `--xyz` to the program, but there is no option named `"xyz"`.
    Unknown,

    /// The named argument requires a parameter, but no parameter was supplied.
    ///
    /// For example, the program accepts `--output=<file>`, but an argument was passed as `--output`
    /// with no parameter.
    MissingParameter,

    /// The named argument does not accept a parameter, but one was supplied.
    ///
    /// For example, the program accepts `--verbose`, but an argument was passed as `--verbose=3`.
    UnexpectedParameter,

    /// The named argument was passed a value which is not valid unicode.
    InvalidUnicode,

    /// The value for the named argument was invalid.
    ///
    /// For example, the program accepts `--jobs=<N>` with integer N, but the user passed in
    /// `--jobs=xyz`.
    InvalidValue(Box<dyn Error>),
}

impl<T> From<T> for OptionError
where
    T: Error + 'static,
{
    fn from(x: T) -> OptionError {
        OptionError::InvalidValue(Box::new(x))
    }
}

/// A command-line usage error, for when the user has passed incorrect arguments to the program.
#[derive(Debug)]
pub enum UsageError<T> {
    /// Indicates an argument has invalid syntax. Used for arguments which cannot be parsed.
    InvalidArgument {
        /// Full text of the argument.
        arg: T,
    },

    /// Indicates an argument was unexpected. Used for positional arguments.
    UnexpectedArgument {
        /// Full text of the argument.
        arg: T,
    },

    /// Indicates an expected positional argument was missing.
    MissingArgument {
        /// The name of the argument.
        name: String,
    },

    /// Indicates an invalid named argument.
    InvalidOption {
        /// The name of the option without any leading dashes.
        name: String,
        /// The option parameter value, if it exists.
        value: Option<T>,
        /// The inner error from parsing the option.
        err: OptionError,
    },
}

impl<T> Display for UsageError<T>
where
    T: Debug,
{
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match self {
            UsageError::InvalidArgument { arg } => write!(f, "invalid argument {:?}", arg),
            UsageError::UnexpectedArgument { arg } => write!(f, "unexpected argument {:?}", arg),
            UsageError::MissingArgument { name } => write!(f, "missing argument <{}>", name),
            UsageError::InvalidOption { name, value, err } => match err {
                OptionError::Unknown => write!(f, "unknown option -{}", name),
                OptionError::MissingParameter => write!(f, "option -{} requires a parameter", name),
                OptionError::UnexpectedParameter => {
                    write!(f, "option -{} does not accept a parameter", name)
                }
                OptionError::InvalidUnicode => write!(
                    f,
                    "invalid value {:?} for option -{}: invalid Unicode string",
                    value.as_ref().unwrap(),
                    name
                ),
                OptionError::InvalidValue(err) => write!(
                    f,
                    "invalid value {:?} for option -{}: {}",
                    value.as_ref().unwrap(),
                    name,
                    err
                ),
            },
        }
    }
}

impl<T> Error for UsageError<T> where T: Debug {}