You've already forked uutils-args
mirror of
https://github.com/uutils/uutils-args.git
synced 2026-06-10 16:13:08 -07:00
add exit code to the error type
This commit is contained in:
@@ -186,7 +186,7 @@ pub fn short_handling(args: &[Argument]) -> (TokenStream, Vec<char>) {
|
||||
Ok(Some(Argument::Custom(
|
||||
match short {
|
||||
#(#match_arms)*
|
||||
_ => return Err(::uutils_args::Error::UnexpectedOption(short.to_string(), Vec::new())),
|
||||
_ => return Err(::uutils_args::ErrorKind::UnexpectedOption(short.to_string(), Vec::new())),
|
||||
}
|
||||
)))
|
||||
);
|
||||
@@ -233,7 +233,7 @@ pub fn long_handling(args: &[Argument], help_flags: &Flags) -> TokenStream {
|
||||
|
||||
if options.is_empty() {
|
||||
return quote!(
|
||||
return Err(::uutils_args::Error::UnexpectedOption(
|
||||
return Err(::uutils_args::ErrorKind::UnexpectedOption(
|
||||
long.to_string(),
|
||||
Vec::new()
|
||||
))
|
||||
@@ -321,7 +321,7 @@ pub fn free_handling(args: &[Argument]) -> TokenStream {
|
||||
if let Some((prefix, value)) = arg.split_once('=') {
|
||||
#(#dd_branches)*
|
||||
|
||||
return Err(::uutils_args::Error::UnexpectedOption(
|
||||
return Err(::uutils_args::ErrorKind::UnexpectedOption(
|
||||
prefix.to_string(),
|
||||
::uutils_args::internal::filter_suggestions(prefix, &[#(#dd_args),*], "")
|
||||
));
|
||||
@@ -392,9 +392,12 @@ pub fn positional_handling(args: &[Argument]) -> (TokenStream, TokenStream) {
|
||||
let mut missing: Vec<&str> = vec![];
|
||||
#(#missing_argument_checks)*
|
||||
if !missing.is_empty() {
|
||||
Err(uutils_args::Error::MissingPositionalArguments(
|
||||
missing.iter().map(ToString::to_string).collect::<Vec<String>>()
|
||||
))
|
||||
Err(uutils_args::Error {
|
||||
exit_code: Self::EXIT_CODE,
|
||||
kind: uutils_args::ErrorKind::MissingPositionalArguments(
|
||||
missing.iter().map(ToString::to_string).collect::<Vec<String>>()
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ pub fn arguments(input: TokenStream) -> TokenStream {
|
||||
#[allow(unreachable_code)]
|
||||
fn next_arg(
|
||||
parser: &mut uutils_args::lexopt::Parser, positional_idx: &mut usize
|
||||
) -> Result<Option<uutils_args::Argument<Self>>, uutils_args::Error> {
|
||||
) -> Result<Option<::uutils_args::Argument<Self>>, ::uutils_args::ErrorKind> {
|
||||
use uutils_args::{Value, lexopt, Error, Argument};
|
||||
|
||||
#free
|
||||
|
||||
+26
-15
@@ -7,8 +7,13 @@ use std::{
|
||||
fmt::{Debug, Display},
|
||||
};
|
||||
|
||||
pub struct Error {
|
||||
pub exit_code: i32,
|
||||
pub kind: ErrorKind,
|
||||
}
|
||||
|
||||
/// Errors that can occur while parsing arguments.
|
||||
pub enum Error {
|
||||
pub enum ErrorKind {
|
||||
/// There was an option that required an option, but none was given.
|
||||
MissingValue {
|
||||
option: Option<String>,
|
||||
@@ -51,53 +56,59 @@ pub enum Error {
|
||||
IoError(std::io::Error),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
impl From<std::io::Error> for ErrorKind {
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
Error::IoError(value)
|
||||
ErrorKind::IoError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.kind.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
impl Display for ErrorKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "error: ")?;
|
||||
match self {
|
||||
Error::MissingValue { option } => match option {
|
||||
ErrorKind::MissingValue { option } => match option {
|
||||
Some(option) => write!(f, "Missing value for '{option}'."),
|
||||
None => write!(f, "Missing value"),
|
||||
},
|
||||
Error::MissingPositionalArguments(args) => {
|
||||
ErrorKind::MissingPositionalArguments(args) => {
|
||||
write!(f, "Missing values for the following positional arguments:")?;
|
||||
for arg in args {
|
||||
write!(f, " - {arg}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Error::UnexpectedOption(opt, suggestions) => {
|
||||
ErrorKind::UnexpectedOption(opt, suggestions) => {
|
||||
write!(f, "Found an invalid option '{opt}'.")?;
|
||||
if !suggestions.is_empty() {
|
||||
write!(f, "\nDid you mean: {}", suggestions.join(", "))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Error::UnexpectedArgument(arg) => {
|
||||
ErrorKind::UnexpectedArgument(arg) => {
|
||||
write!(f, "Found an invalid argument '{}'.", arg.to_string_lossy())
|
||||
}
|
||||
Error::UnexpectedValue { option, value } => {
|
||||
ErrorKind::UnexpectedValue { option, value } => {
|
||||
write!(
|
||||
f,
|
||||
"Got an unexpected value '{}' for option '{option}'.",
|
||||
value.to_string_lossy(),
|
||||
)
|
||||
}
|
||||
Error::ParsingFailed {
|
||||
ErrorKind::ParsingFailed {
|
||||
option,
|
||||
value,
|
||||
error,
|
||||
@@ -110,7 +121,7 @@ impl Display for Error {
|
||||
write!(f, "Invalid value '{value}' for '{option}': {error}")
|
||||
}
|
||||
}
|
||||
Error::AmbiguousOption { option, candidates } => {
|
||||
ErrorKind::AmbiguousOption { option, candidates } => {
|
||||
write!(
|
||||
f,
|
||||
"Option '{option}' is ambiguous. The following candidates match:"
|
||||
@@ -120,16 +131,16 @@ impl Display for Error {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Error::NonUnicodeValue(x) => {
|
||||
ErrorKind::NonUnicodeValue(x) => {
|
||||
write!(f, "Invalid unicode value found: {}", x.to_string_lossy())
|
||||
}
|
||||
Error::IoError(x) => std::fmt::Display::fmt(x, f),
|
||||
ErrorKind::IoError(x) => std::fmt::Display::fmt(x, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<lexopt::Error> for Error {
|
||||
fn from(other: lexopt::Error) -> Error {
|
||||
impl From<lexopt::Error> for ErrorKind {
|
||||
fn from(other: lexopt::Error) -> ErrorKind {
|
||||
match other {
|
||||
lexopt::Error::MissingValue { option } => Self::MissingValue { option },
|
||||
lexopt::Error::UnexpectedOption(s) => Self::UnexpectedOption(s, Vec::new()),
|
||||
|
||||
+7
-6
@@ -10,7 +10,8 @@
|
||||
//! Yet, they should be properly documented to make macro-expanded code
|
||||
//! readable.
|
||||
|
||||
use super::{Error, Value};
|
||||
use crate::error::ErrorKind;
|
||||
use crate::value::Value;
|
||||
use std::{
|
||||
ffi::{OsStr, OsString},
|
||||
io::Write,
|
||||
@@ -64,8 +65,8 @@ pub fn parse_prefix<T: Value>(parser: &mut lexopt::Parser, prefix: &'static str)
|
||||
}
|
||||
|
||||
/// Parse a value and wrap the error into an `Error::ParsingFailed`
|
||||
pub fn parse_value_for_option<T: Value>(opt: &str, v: &OsStr) -> Result<T, Error> {
|
||||
T::from_value(v).map_err(|e| Error::ParsingFailed {
|
||||
pub fn parse_value_for_option<T: Value>(opt: &str, v: &OsStr) -> Result<T, ErrorKind> {
|
||||
T::from_value(v).map_err(|e| ErrorKind::ParsingFailed {
|
||||
option: opt.into(),
|
||||
value: v.to_string_lossy().to_string(),
|
||||
error: e,
|
||||
@@ -76,7 +77,7 @@ pub fn parse_value_for_option<T: Value>(opt: &str, v: &OsStr) -> Result<T, Error
|
||||
pub fn infer_long_option<'a>(
|
||||
input: &'a str,
|
||||
long_options: &'a [&'a str],
|
||||
) -> Result<&'a str, Error> {
|
||||
) -> Result<&'a str, ErrorKind> {
|
||||
let mut candidates = Vec::new();
|
||||
let mut exact_match = None;
|
||||
for opt in long_options {
|
||||
@@ -91,11 +92,11 @@ pub fn infer_long_option<'a>(
|
||||
match (exact_match, &candidates[..]) {
|
||||
(Some(opt), _) => Ok(*opt),
|
||||
(None, [opt]) => Ok(**opt),
|
||||
(None, []) => Err(Error::UnexpectedOption(
|
||||
(None, []) => Err(ErrorKind::UnexpectedOption(
|
||||
format!("--{input}"),
|
||||
filter_suggestions(input, long_options, "--"),
|
||||
)),
|
||||
(None, _) => Err(Error::AmbiguousOption {
|
||||
(None, _) => Err(ErrorKind::AmbiguousOption {
|
||||
option: input.to_string(),
|
||||
candidates: candidates.iter().map(|s| s.to_string()).collect(),
|
||||
}),
|
||||
|
||||
+13
-8
@@ -10,7 +10,7 @@ mod value;
|
||||
pub use lexopt;
|
||||
pub use uutils_args_derive::*;
|
||||
|
||||
pub use error::Error;
|
||||
pub use error::{Error, ErrorKind};
|
||||
pub use value::{Value, ValueError, ValueResult};
|
||||
|
||||
use std::{ffi::OsString, marker::PhantomData};
|
||||
@@ -24,12 +24,12 @@ pub enum Argument<T: Arguments> {
|
||||
Custom(T),
|
||||
}
|
||||
|
||||
fn exit_if_err<T>(res: Result<T, Error>, exit_code: i32) -> T {
|
||||
fn exit_if_err<T>(res: Result<T, Error>) -> T {
|
||||
match res {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
eprintln!("{err}");
|
||||
std::process::exit(exit_code);
|
||||
std::process::exit(err.exit_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ pub trait Arguments: Sized {
|
||||
fn next_arg(
|
||||
parser: &mut lexopt::Parser,
|
||||
positional_idx: &mut usize,
|
||||
) -> Result<Option<Argument<Self>>, Error>;
|
||||
) -> Result<Option<Argument<Self>>, ErrorKind>;
|
||||
|
||||
/// Check for any required arguments that have not been found.
|
||||
///
|
||||
@@ -89,7 +89,7 @@ pub trait Arguments: Sized {
|
||||
I: IntoIterator + 'static,
|
||||
I::Item: Into<OsString>,
|
||||
{
|
||||
exit_if_err(Self::try_check(args), Self::EXIT_CODE)
|
||||
exit_if_err(Self::try_check(args))
|
||||
}
|
||||
|
||||
/// Check all arguments immediately and return any errors.
|
||||
@@ -135,10 +135,15 @@ impl<T: Arguments> ArgumentIter<T> {
|
||||
}
|
||||
|
||||
pub fn next_arg(&mut self) -> Result<Option<T>, Error> {
|
||||
if let Some(arg) = T::next_arg(&mut self.parser, &mut self.positional_idx)? {
|
||||
if let Some(arg) =
|
||||
T::next_arg(&mut self.parser, &mut self.positional_idx).map_err(|kind| Error {
|
||||
exit_code: T::EXIT_CODE,
|
||||
kind,
|
||||
})?
|
||||
{
|
||||
match arg {
|
||||
Argument::Help => {
|
||||
self.help()?;
|
||||
self.help().unwrap();
|
||||
std::process::exit(0);
|
||||
}
|
||||
Argument::Version => {
|
||||
@@ -184,7 +189,7 @@ pub trait Options<Arg: Arguments>: Sized {
|
||||
I: IntoIterator + 'static,
|
||||
I::Item: Into<OsString>,
|
||||
{
|
||||
exit_if_err(self.try_parse(args), Arg::EXIT_CODE)
|
||||
exit_if_err(self.try_parse(args))
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
|
||||
+7
-3
@@ -1,7 +1,7 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use std::{
|
||||
ffi::{OsStr, OsString},
|
||||
path::PathBuf,
|
||||
@@ -50,7 +50,7 @@ impl std::fmt::Display for ValueError {
|
||||
|
||||
/// Defines how a type should be parsed from an argument.
|
||||
///
|
||||
/// If an error is returned, it will be wrapped in [`Error::ParsingFailed`]
|
||||
/// If an error is returned, it will be wrapped in [`ErrorKind::ParsingFailed`]
|
||||
pub trait Value: Sized {
|
||||
fn from_value(value: &OsStr) -> ValueResult<Self>;
|
||||
|
||||
@@ -81,7 +81,11 @@ impl Value for String {
|
||||
fn from_value(value: &OsStr) -> ValueResult<Self> {
|
||||
match value.to_str() {
|
||||
Some(s) => Ok(s.into()),
|
||||
None => Err(Error::NonUnicodeValue(value.into()).into()),
|
||||
None => Err(Error {
|
||||
exit_code: 1,
|
||||
kind: ErrorKind::NonUnicodeValue(value.into()),
|
||||
}
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user