feat(command_parser): allow aliases in flags

This commit is contained in:
dusk 2025-01-22 00:50:17 +09:00
parent bf5e448aad
commit ff6dc12cae
No known key found for this signature in database
4 changed files with 51 additions and 17 deletions

View file

@ -4,7 +4,7 @@ pub fn cmds() -> impl Iterator<Item = Command> {
let help = ["help", "h"]; let help = ["help", "h"];
[ [
command!([help] => "help") command!([help] => "help")
.value_flag("foo", OpaqueString) // todo: just for testing .flag(("foo", OpaqueString)) // todo: just for testing
.help("Shows the help command"), .help("Shows the help command"),
command!([help, "commands"] => "help_commands").help("help commands"), command!([help, "commands"] => "help_commands").help("help commands"),
command!([help, "proxy"] => "help_proxy").help("help proxy"), command!([help, "proxy"] => "help_proxy").help("help proxy"),

View file

@ -2,7 +2,7 @@ use std::fmt::{Debug, Display};
use smol_str::SmolStr; use smol_str::SmolStr;
use crate::{flag::Flag, parameter::*, token::Token}; use crate::{flag::Flag, token::Token};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Command { pub struct Command {
@ -57,13 +57,8 @@ impl Command {
self self
} }
pub fn flag(mut self, name: impl Into<SmolStr>) -> Self { pub fn flag(mut self, flag: impl Into<Flag>) -> Self {
self.flags.push(Flag::new(name)); self.flags.push(flag.into());
self
}
pub fn value_flag(mut self, name: impl Into<SmolStr>, value: ParameterKind) -> Self {
self.flags.push(Flag::new(name).with_value(value));
self self
} }
} }

View file

@ -13,6 +13,7 @@ pub enum FlagValueMatchError {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Flag { pub struct Flag {
name: SmolStr, name: SmolStr,
aliases: Vec<SmolStr>,
value: Option<ParameterKind>, value: Option<ParameterKind>,
} }
@ -38,26 +39,36 @@ impl Flag {
pub fn new(name: impl Into<SmolStr>) -> Self { pub fn new(name: impl Into<SmolStr>) -> Self {
Self { Self {
name: name.into(), name: name.into(),
aliases: Vec::new(),
value: None, value: None,
} }
} }
pub fn with_value(mut self, param: ParameterKind) -> Self { pub fn value(mut self, param: ParameterKind) -> Self {
self.value = Some(param); self.value = Some(param);
self self
} }
pub fn name(&self) -> &str { pub fn alias(mut self, alias: impl Into<SmolStr>) -> Self {
self.aliases.push(alias.into());
self
}
pub fn get_name(&self) -> &str {
&self.name &self.name
} }
pub fn value_kind(&self) -> Option<ParameterKind> { pub fn get_value(&self) -> Option<ParameterKind> {
self.value self.value
} }
pub fn get_aliases(&self) -> impl Iterator<Item = &str> {
self.aliases.iter().map(|s| s.as_str())
}
pub fn try_match(&self, input_name: &str, input_value: Option<&str>) -> TryMatchFlagResult { pub fn try_match(&self, input_name: &str, input_value: Option<&str>) -> TryMatchFlagResult {
// if not matching flag then skip anymore matching // if not matching the name or any aliases then skip anymore matching
if self.name != input_name { if self.name != input_name && self.get_aliases().all(|s| s.ne(input_name)) {
return None; return None;
} }
// get token to try matching with, if flag doesn't have one then that means it is matched (it is without any value) // get token to try matching with, if flag doesn't have one then that means it is matched (it is without any value)
@ -82,3 +93,31 @@ impl Flag {
} }
} }
} }
impl From<&str> for Flag {
fn from(name: &str) -> Self {
Flag::new(name)
}
}
impl From<(&str, ParameterKind)> for Flag {
fn from((name, value): (&str, ParameterKind)) -> Self {
Flag::new(name).value(value)
}
}
impl<const L: usize> From<[&str; L]> for Flag {
fn from(value: [&str; L]) -> Self {
let mut flag = Flag::new(value[0]);
for alias in &value[1..] {
flag = flag.alias(*alias);
}
flag
}
}
impl<const L: usize> From<([&str; L], ParameterKind)> for Flag {
fn from((names, value): ([&str; L], ParameterKind)) -> Self {
Flag::from(names).value(value)
}
}

View file

@ -133,7 +133,7 @@ pub fn parse_command(
FlagMatchError::ValueMatchFailed(FlagValueMatchError::ValueMissing) => { FlagMatchError::ValueMatchFailed(FlagValueMatchError::ValueMissing) => {
format!( format!(
"Flag `-{name}` in command `{prefix}{input}` is missing a value, try passing `{flag}`.", "Flag `-{name}` in command `{prefix}{input}` is missing a value, try passing `{flag}`.",
name = flag.name() name = flag.get_name()
) )
} }
FlagMatchError::ValueMatchFailed( FlagMatchError::ValueMatchFailed(
@ -141,7 +141,7 @@ pub fn parse_command(
) => { ) => {
format!( format!(
"Flag `-{name}` in command `{prefix}{input}` has a value (`{raw}`) that could not be parsed: {msg}.", "Flag `-{name}` in command `{prefix}{input}` has a value (`{raw}`) that could not be parsed: {msg}.",
name = flag.name() name = flag.get_name()
) )
} }
}; };
@ -210,7 +210,7 @@ fn match_flag<'a>(
for flag in possible_flags { for flag in possible_flags {
println!("matching flag {flag:?}"); println!("matching flag {flag:?}");
match flag.try_match(matched_flag.name, matched_flag.value) { match flag.try_match(matched_flag.name, matched_flag.value) {
Some(Ok(param)) => return Some(Ok((flag.name().into(), param))), Some(Ok(param)) => return Some(Ok((flag.get_name().into(), param))),
Some(Err(err)) => return Some(Err((flag, err))), Some(Err(err)) => return Some(Err((flag, err))),
None => {} None => {}
} }