#![forbid(unsafe_code)]
#![deny(clippy::all)]
#![deny(clippy::pedantic)]
use std::env;
use std::ffi::OsString;
use std::fmt::Display;
pub mod traits;
#[derive(Debug)]
pub enum CliError {
MissingValue(String),
MissingRequired(String),
ParseBoolError(String, OsString, std::str::ParseBoolError),
ParseCharError(String, OsString, std::char::ParseCharError),
ParseFloatError(String, OsString, std::num::ParseFloatError),
ParseIntError(String, OsString, std::num::ParseIntError),
ParseStrError(String, OsString),
Unknown(OsString),
}
pub trait OnlyArgs {
const HELP: &'static str = concat!(
env!("CARGO_PKG_NAME"),
" v",
env!("CARGO_PKG_VERSION"),
"\n",
env!("CARGO_PKG_DESCRIPTION"),
"\n",
);
const VERSION: &'static str = concat!(
env!("CARGO_PKG_NAME"),
" v",
env!("CARGO_PKG_VERSION"),
"\n",
);
fn parse(args: Vec<OsString>) -> Result<Self, CliError>
where
Self: Sized;
fn help() -> ! {
eprintln!("{}", Self::HELP);
std::process::exit(0);
}
fn version() -> ! {
eprintln!("{}", Self::VERSION);
std::process::exit(0);
}
}
impl Display for CliError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MissingValue(arg) => write!(f, "Missing value for argument `{arg}`"),
Self::MissingRequired(arg) => write!(f, "Missing required argument `{arg}`"),
Self::ParseBoolError(arg, value, _) => write!(
f,
"Bool parsing error for argument `{arg}`: value={value:?}"
),
Self::ParseCharError(arg, value, _) => write!(
f,
"Char parsing error for argument `{arg}`: value={value:?}"
),
Self::ParseFloatError(arg, value, _) => write!(
f,
"Float parsing error for argument `{arg}`: value={value:?}"
),
Self::ParseIntError(arg, value, _) => {
write!(f, "Int parsing error for argument `{arg}`: value={value:?}")
}
Self::ParseStrError(arg, value) => write!(
f,
"String parsing error for argument `{arg}`: value={value:?}"
),
Self::Unknown(arg) => write!(f, "Unknown argument: {arg:?}"),
}
}
}
impl std::error::Error for CliError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::ParseBoolError(_, _, err) => Some(err),
Self::ParseCharError(_, _, err) => Some(err),
Self::ParseFloatError(_, _, err) => Some(err),
Self::ParseIntError(_, _, err) => Some(err),
_ => None,
}
}
}
pub fn parse<T: OnlyArgs>() -> Result<T, CliError> {
T::parse(env::args_os().skip(1).collect())
}
mod macros {
#[macro_export]
macro_rules! impl_help {
() => {
concat!(
env!("CARGO_PKG_NAME"),
" v",
env!("CARGO_PKG_VERSION"),
"\n",
env!("CARGO_PKG_DESCRIPTION"),
"\n",
)
};
}
#[macro_export]
macro_rules! impl_version {
() => {
concat!(
env!("CARGO_PKG_NAME"),
" v",
env!("CARGO_PKG_VERSION"),
"\n",
);
};
}
}