use std::{ error::Error as StdError, ffi::OsString, fmt::{Debug, Display}, }; /// Errors that can occur while parsing arguments. pub enum Error { /// There was an option that required an option, but none was given. MissingValue { option: Option, }, /// Some positional arguments were not given. MissingPositionalArguments(Vec), /// An unrecognized option was passed. UnexpectedOption(String), /// No more positional arguments were expected, but one was given anyway. UnexpectedArgument(OsString), /// A value was passed to an option that didn't expect a value. UnexpectedValue { option: String, value: OsString, }, /// Parsing of a value failed. ParsingFailed { option: String, value: String, error: Box, }, /// An abbreviated long option was given that could match multiple /// long options. AmbiguousOption { option: String, candidates: Vec, }, /// The value was required to be valid UTF-8, but it wasn't. NonUnicodeValue(OsString), IoError(std::io::Error), } impl From for Error { fn from(value: std::io::Error) -> Self { Error::IoError(value) } } impl StdError for Error {} impl Debug for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self, f) } } impl Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "error: ")?; match self { Error::MissingValue { option } => match option { Some(option) => write!(f, "Missing value for '{option}'."), None => write!(f, "Missing value"), }, Error::MissingPositionalArguments(args) => { write!(f, "Missing values for the following positional arguments:")?; for arg in args { write!(f, " - {arg}")?; } Ok(()) } Error::UnexpectedOption(opt) => { write!(f, "Found an invalid option '{opt}'.") } Error::UnexpectedArgument(arg) => { write!(f, "Found an invalid argument '{}'.", arg.to_string_lossy()) } Error::UnexpectedValue { option, value } => { write!( f, "Got an unexpected value '{}' for option '{option}'.", value.to_string_lossy(), ) } Error::ParsingFailed { option, value, error, } => { // TODO: option should not not be Option, because even for positional // arguments we want to specify the name of the value. if option.is_empty() { write!(f, "Invalid value '{value}': {error}") } else { write!(f, "Invalid value '{value}' for '{option}': {error}") } } Error::AmbiguousOption { option, candidates } => { write!( f, "Option '{option}' is ambiguous. The following candidates match:" )?; for candidate in candidates { write!(f, " - {candidate}")?; } Ok(()) } Error::NonUnicodeValue(x) => { write!(f, "Invalid unicode value found: {}", x.to_string_lossy()) } Error::IoError(x) => std::fmt::Display::fmt(x, f), } } } impl From for Error { fn from(other: lexopt::Error) -> Error { match other { lexopt::Error::MissingValue { option } => Self::MissingValue { option }, lexopt::Error::UnexpectedOption(s) => Self::UnexpectedOption(s), lexopt::Error::UnexpectedArgument(s) => Self::UnexpectedArgument(s), lexopt::Error::UnexpectedValue { option, value } => { Self::UnexpectedValue { option, value } } lexopt::Error::NonUnicodeValue(s) => Self::NonUnicodeValue(s), lexopt::Error::ParsingFailed { .. } | lexopt::Error::Custom(_) => { panic!("Should never be constructed.") } } } }