use std::fmt;
use crate::parser::ArgParser;
use crate::validators::Validator;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FlagDef {
pub long: String,
pub short: Option<char>,
pub description: String,
pub hidden: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OptionDef {
pub long: String,
pub short: Option<char>,
pub placeholder: String,
pub description: String,
pub required: bool,
pub default: Option<String>,
pub env_var: Option<String>,
pub multi: bool,
pub hidden: bool,
pub validator: Option<Validator>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PositionalDef {
pub name: String,
pub description: String,
pub required: bool,
pub default: Option<String>,
pub multi: bool,
pub validator: Option<Validator>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SubcommandDef {
pub name: String,
pub description: String,
pub parser: ArgParser,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GroupDef {
pub name: String,
pub members: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ConflictDef {
pub name: String,
pub members: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ParseError {
MissingRequired(String),
MissingValue(String),
UnknownArgument(String),
InvalidFormat(String),
HelpRequested(String),
VersionRequested(String),
NoSubcommand(String),
UnknownSubcommand(String),
DuplicateOption(String),
InvalidUtf8(String),
ValidationFailed {
name: String,
message: String,
},
GroupViolation {
group: String,
members: Vec<String>,
},
ConflictViolation {
conflict: String,
provided: Vec<String>,
},
}
#[cfg(feature = "color")]
fn error_prefix() -> String {
use nanocolor::Colorize;
format!("{} ", "error:".bold().red())
}
#[cfg(not(feature = "color"))]
fn error_prefix() -> String {
String::new()
}
#[cfg(feature = "color")]
fn yellow_arg(s: &str) -> String {
use nanocolor::Colorize;
s.yellow().to_string()
}
#[cfg(not(feature = "color"))]
fn yellow_arg(s: &str) -> String {
s.to_string()
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseError::HelpRequested(text) | ParseError::VersionRequested(text) => {
write!(f, "{text}")
}
ParseError::MissingRequired(name) => {
write!(f, "{}missing required argument: {}", error_prefix(), yellow_arg(name))
}
ParseError::MissingValue(name) => {
write!(
f,
"{}missing value for option: {}",
error_prefix(),
yellow_arg(&format!("--{name}"))
)
}
ParseError::UnknownArgument(token) => {
write!(f, "{}unknown argument: {}", error_prefix(), yellow_arg(token))
}
ParseError::InvalidFormat(msg) => {
write!(f, "{}invalid format: {msg}", error_prefix())
}
ParseError::NoSubcommand(names) => {
write!(f, "{}no subcommand provided. Available: {names}", error_prefix())
}
ParseError::UnknownSubcommand(name) => {
write!(f, "{}unknown subcommand: {}", error_prefix(), yellow_arg(name))
}
ParseError::DuplicateOption(name) => {
write!(
f,
"{}option {} was provided more than once (use .multi() to allow repeats)",
error_prefix(),
yellow_arg(&format!("--{name}"))
)
}
ParseError::InvalidUtf8(lossy) => {
write!(
f,
"{}argument is not valid UTF-8: {}",
error_prefix(),
yellow_arg(lossy)
)
}
ParseError::ValidationFailed { name, message } => {
write!(
f,
"{}validation failed for {}: {message}",
error_prefix(),
yellow_arg(name)
)
}
ParseError::GroupViolation { group, members } => {
let member_list: Vec<String> = members.iter().map(|m| yellow_arg(&format!("--{m}"))).collect();
write!(
f,
"{}at least one of the following is required (group '{group}'): {}",
error_prefix(),
member_list.join(", ")
)
}
ParseError::ConflictViolation { conflict, provided } => {
let arg_list: Vec<String> = provided.iter().map(|m| yellow_arg(&format!("--{m}"))).collect();
let joined = if arg_list.len() == 2 {
format!("{} and {}", arg_list[0], arg_list[1])
} else {
arg_list.join(", ")
};
write!(
f,
"{}conflicting arguments ('{conflict}'): {joined} cannot be used together",
error_prefix(),
)
}
}
}
}
impl std::error::Error for ParseError {}