use anyhow::{Result, bail};
use owo_colors::OwoColorize;
use serde::{Deserialize, de::Error};
use std::{collections::VecDeque, fmt::Display};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArgumentDefinition {
pub name: String,
pub arg_type: ArgumentType,
}
impl<'de> Deserialize<'de> for ArgumentDefinition {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let arg = String::deserialize(deserializer)?;
Self::from_string::<D>(arg)
}
}
impl ArgumentDefinition {
pub fn from_string<'de, D>(mut arg: String) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let first = arg
.chars()
.next()
.ok_or_else(|| Error::custom("Empty argument"))?;
let arg_type = match first {
'?' => ArgumentType::Optional,
'*' => ArgumentType::Variadic,
'+' => ArgumentType::RequiredVariadic,
_ => ArgumentType::Required,
};
if arg_type != ArgumentType::Required {
arg.remove(0); }
Ok(Self {
name: arg,
arg_type,
})
}
pub fn summarize(&self, color: bool) -> (String, usize) {
let symbol = match self.arg_type {
ArgumentType::Required => "",
ArgumentType::Optional => "?",
ArgumentType::Variadic => "*",
ArgumentType::RequiredVariadic => "+",
};
let len = self.name.len() + symbol.len();
let summary = if color {
format!("{}{}", symbol.magenta(), self.name.cyan())
} else {
format!("{}{}", symbol, self.name)
};
(summary, len)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ArgumentType {
Required,
Optional,
Variadic,
RequiredVariadic,
}
impl ArgumentType {
pub fn resolve(&self, args: &mut VecDeque<String>) -> Result<ResolvedArgument> {
match self {
Self::Required => {
let Some(value) = args.pop_front() else {
bail!("Required argument not provided");
};
Ok(ResolvedArgument::Required(value))
}
Self::Optional => {
let value = args.pop_front();
Ok(ResolvedArgument::Optional(value))
}
Self::Variadic => {
let values: Vec<String> = args.drain(..).collect();
Ok(ResolvedArgument::Variadic(values))
}
Self::RequiredVariadic => {
if args.is_empty() {
bail!("Required variadic argument must contain at least one value");
}
let values: Vec<String> = args.drain(..).collect();
Ok(ResolvedArgument::RequiredVariadic(values))
}
}
}
}
impl Display for ArgumentType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Required => write!(f, "Required"),
Self::Optional => write!(f, "?Optional"),
Self::Variadic => write!(f, "*Variadic"),
Self::RequiredVariadic => write!(f, "+RequiredVariadic"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResolvedArgument {
Required(String),
Optional(Option<String>),
Variadic(Vec<String>),
RequiredVariadic(Vec<String>),
}
impl ResolvedArgument {
pub const fn arg_type(&self) -> ArgumentType {
match self {
Self::Required(_) => ArgumentType::Required,
Self::Optional(_) => ArgumentType::Optional,
Self::Variadic(_) => ArgumentType::Variadic,
Self::RequiredVariadic(_) => ArgumentType::RequiredVariadic,
}
}
pub fn matches(&self, arg_type: &ArgumentType) -> bool {
self.arg_type() == *arg_type
}
#[allow(clippy::bool_to_int_with_if, reason = "Const fn")]
pub const fn len(&self) -> usize {
match self {
Self::Required(_) => 1,
Self::Optional(option) => {
if option.is_some() {
1
} else {
0
}
}
Self::Variadic(vec) | Self::RequiredVariadic(vec) => vec.len(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_argument_resolving_1() {
let mut args = VecDeque::from(vec![
"arg1".to_string(),
"arg2".to_string(),
"arg3".to_string(),
]);
let arg = ArgumentType::Optional.resolve(&mut args).unwrap();
assert_eq!(arg, ResolvedArgument::Optional(Some("arg1".to_string())));
assert_eq!(
args,
VecDeque::from(vec!["arg2".to_string(), "arg3".to_string()])
);
let arg = ArgumentType::Required.resolve(&mut args).unwrap();
assert_eq!(arg, ResolvedArgument::Required("arg2".to_string()));
assert_eq!(args, VecDeque::from(vec!["arg3".to_string()]));
let arg = ArgumentType::Optional.resolve(&mut args).unwrap();
assert_eq!(arg, ResolvedArgument::Optional(Some("arg3".to_string())));
assert_eq!(args, VecDeque::from(vec![]));
let err = ArgumentType::Required.resolve(&mut args).unwrap_err();
assert_eq!(err.to_string(), "Required argument not provided");
assert_eq!(args, VecDeque::from(vec![]));
let arg = ArgumentType::Optional.resolve(&mut args).unwrap();
assert_eq!(arg, ResolvedArgument::Optional(None));
assert_eq!(args, VecDeque::from(vec![]));
}
#[test]
fn test_argument_resolving_2() {
let mut args = VecDeque::from(vec![
"arg1".to_string(),
"arg2".to_string(),
"arg3".to_string(),
]);
let arg = ArgumentType::Variadic.resolve(&mut args).unwrap();
assert_eq!(
arg,
ResolvedArgument::Variadic(vec![
"arg1".to_string(),
"arg2".to_string(),
"arg3".to_string()
])
);
assert_eq!(args, VecDeque::from(vec![]));
let err = ArgumentType::RequiredVariadic
.resolve(&mut args)
.unwrap_err();
assert_eq!(
err.to_string(),
"Required variadic argument must contain at least one value"
);
assert_eq!(args, VecDeque::from(vec![]));
}
}