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"];
[
command!([help] => "help")
.value_flag("foo", OpaqueString) // todo: just for testing
.flag(("foo", OpaqueString)) // todo: just for testing
.help("Shows the help command"),
command!([help, "commands"] => "help_commands").help("help commands"),
command!([help, "proxy"] => "help_proxy").help("help proxy"),

View file

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

View file

@ -13,6 +13,7 @@ pub enum FlagValueMatchError {
#[derive(Debug, Clone)]
pub struct Flag {
name: SmolStr,
aliases: Vec<SmolStr>,
value: Option<ParameterKind>,
}
@ -38,26 +39,36 @@ impl Flag {
pub fn new(name: impl Into<SmolStr>) -> Self {
Self {
name: name.into(),
aliases: Vec::new(),
value: None,
}
}
pub fn with_value(mut self, param: ParameterKind) -> Self {
pub fn value(mut self, param: ParameterKind) -> Self {
self.value = Some(param);
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
}
pub fn value_kind(&self) -> Option<ParameterKind> {
pub fn get_value(&self) -> Option<ParameterKind> {
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 {
// if not matching flag then skip anymore matching
if self.name != input_name {
// if not matching the name or any aliases then skip anymore matching
if self.name != input_name && self.get_aliases().all(|s| s.ne(input_name)) {
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)
@ -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) => {
format!(
"Flag `-{name}` in command `{prefix}{input}` is missing a value, try passing `{flag}`.",
name = flag.name()
name = flag.get_name()
)
}
FlagMatchError::ValueMatchFailed(
@ -141,7 +141,7 @@ pub fn parse_command(
) => {
format!(
"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 {
println!("matching flag {flag:?}");
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))),
None => {}
}