Files
Terts Diepraam 0d7c1b747f remove markdown rendering and make help write to stdout
The markdown rendering is too complicated at the moment
and slows the rest of development down too much. We can
add it back in later.

The generated code for the help string now writes
directly to stdout, instead of building up a String,
this leads to nicer code and is probably faster.
2023-06-04 14:09:07 +02:00

138 lines
4.4 KiB
Rust

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<String>,
},
/// Some positional arguments were not given.
MissingPositionalArguments(Vec<String>),
/// 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<dyn StdError + Send + Sync + 'static>,
},
/// An abbreviated long option was given that could match multiple
/// long options.
AmbiguousOption {
option: String,
candidates: Vec<String>,
},
/// The value was required to be valid UTF-8, but it wasn't.
NonUnicodeValue(OsString),
IoError(std::io::Error),
}
impl From<std::io::Error> 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<String>, 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<lexopt::Error> 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.")
}
}
}
}