2025-01-12 04:23:46 +09:00
|
|
|
use std::fmt::{Debug, Display};
|
2025-01-08 18:31:59 +09:00
|
|
|
|
2025-01-04 07:43:55 +09:00
|
|
|
use smol_str::SmolStr;
|
|
|
|
|
|
2025-01-22 00:50:17 +09:00
|
|
|
use crate::{flag::Flag, token::Token};
|
2025-01-04 07:35:04 +09:00
|
|
|
|
2025-01-12 04:23:46 +09:00
|
|
|
#[derive(Debug, Clone)]
|
2025-01-04 07:35:04 +09:00
|
|
|
pub struct Command {
|
|
|
|
|
// TODO: fix hygiene
|
|
|
|
|
pub tokens: Vec<Token>,
|
2025-01-14 11:53:56 +09:00
|
|
|
pub flags: Vec<Flag>,
|
2025-01-04 07:43:55 +09:00
|
|
|
pub help: SmolStr,
|
|
|
|
|
pub cb: SmolStr,
|
2025-01-11 23:05:29 +09:00
|
|
|
pub show_in_suggestions: bool,
|
2025-01-14 11:53:56 +09:00
|
|
|
pub parse_flags_before: usize,
|
2025-01-04 07:35:04 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Command {
|
2025-01-20 22:50:45 +09:00
|
|
|
pub fn new(tokens: impl IntoIterator<Item = Token>, cb: impl Into<SmolStr>) -> Self {
|
2025-01-14 11:53:56 +09:00
|
|
|
let tokens = tokens.into_iter().collect::<Vec<_>>();
|
|
|
|
|
assert!(tokens.len() > 0);
|
2025-01-21 00:39:25 +09:00
|
|
|
// figure out which token to parse / put flags after
|
|
|
|
|
// (by default, put flags after the last token)
|
2025-01-14 11:53:56 +09:00
|
|
|
let mut parse_flags_before = tokens.len();
|
|
|
|
|
let mut was_parameter = true;
|
|
|
|
|
for (idx, token) in tokens.iter().enumerate().rev() {
|
|
|
|
|
match token {
|
2025-01-21 00:39:25 +09:00
|
|
|
// we want flags to go before any parameters
|
2025-01-21 12:36:54 +09:00
|
|
|
Token::Parameter(_) => {
|
2025-01-14 11:53:56 +09:00
|
|
|
parse_flags_before = idx;
|
|
|
|
|
was_parameter = true;
|
|
|
|
|
}
|
2025-01-22 02:12:17 +09:00
|
|
|
Token::Empty | Token::Value { .. } => {
|
2025-01-14 11:53:56 +09:00
|
|
|
if was_parameter {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-01-04 07:35:04 +09:00
|
|
|
Self {
|
2025-01-14 11:53:56 +09:00
|
|
|
flags: Vec::new(),
|
2025-01-20 22:50:45 +09:00
|
|
|
help: SmolStr::new_static("<no help text>"),
|
2025-01-04 07:43:55 +09:00
|
|
|
cb: cb.into(),
|
2025-01-14 11:53:56 +09:00
|
|
|
show_in_suggestions: true,
|
|
|
|
|
parse_flags_before,
|
|
|
|
|
tokens,
|
2025-01-04 07:35:04 +09:00
|
|
|
}
|
|
|
|
|
}
|
2025-01-14 11:53:56 +09:00
|
|
|
|
2025-01-20 22:50:45 +09:00
|
|
|
pub fn help(mut self, v: impl Into<SmolStr>) -> Self {
|
|
|
|
|
self.help = v.into();
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-14 11:53:56 +09:00
|
|
|
pub fn show_in_suggestions(mut self, v: bool) -> Self {
|
|
|
|
|
self.show_in_suggestions = v;
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-22 00:50:17 +09:00
|
|
|
pub fn flag(mut self, flag: impl Into<Flag>) -> Self {
|
|
|
|
|
self.flags.push(flag.into());
|
2025-01-14 11:53:56 +09:00
|
|
|
self
|
|
|
|
|
}
|
2025-01-04 07:35:04 +09:00
|
|
|
}
|
|
|
|
|
|
2025-01-08 18:31:59 +09:00
|
|
|
impl Display for Command {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
for (idx, token) in self.tokens.iter().enumerate() {
|
2025-01-14 11:53:56 +09:00
|
|
|
if idx == self.parse_flags_before {
|
|
|
|
|
for flag in &self.flags {
|
|
|
|
|
write!(f, "[{flag}] ")?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
write!(
|
|
|
|
|
f,
|
|
|
|
|
"{token}{}",
|
|
|
|
|
(idx < self.tokens.len() - 1).then_some(" ").unwrap_or("")
|
|
|
|
|
)?;
|
|
|
|
|
}
|
|
|
|
|
if self.tokens.len() == self.parse_flags_before {
|
|
|
|
|
for flag in &self.flags {
|
|
|
|
|
write!(f, " [{flag}]")?;
|
2025-01-08 18:31:59 +09:00
|
|
|
}
|
|
|
|
|
}
|
2025-01-11 22:38:29 +09:00
|
|
|
Ok(())
|
2025-01-08 18:31:59 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-14 11:53:56 +09:00
|
|
|
// a macro is required because generic cant be different types at the same time (which means you couldnt have ["member", MemberRef, "subcmd"] etc)
|
|
|
|
|
// (and something like &dyn Trait would require everything to be referenced which doesnt look nice anyway)
|
2025-01-04 07:35:04 +09:00
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! command {
|
2025-01-21 12:36:54 +09:00
|
|
|
([$($v:expr),+] => $cb:expr$(,)*) => {
|
|
|
|
|
$crate::command::Command::new($crate::tokens!($($v),+), $cb)
|
|
|
|
|
};
|
|
|
|
|
($tokens:expr => $cb:expr$(,)*) => {
|
|
|
|
|
$crate::command::Command::new($tokens.clone(), $cb)
|
|
|
|
|
};
|
|
|
|
|
($tokens:expr, $($v:expr),+ => $cb:expr$(,)*) => {
|
|
|
|
|
$crate::command::Command::new($crate::concat_tokens!($tokens.clone(), [$($v),+]), $cb)
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! tokens {
|
|
|
|
|
($($v:expr),+$(,)*) => {
|
|
|
|
|
[$($crate::token::Token::from($v)),+]
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! concat_tokens {
|
|
|
|
|
($tokens:expr, [$($v:expr),+]$(,)*) => {
|
|
|
|
|
$tokens.clone().into_iter().chain($crate::tokens!($($v),+).into_iter()).collect::<Vec<_>>()
|
2025-01-04 07:35:04 +09:00
|
|
|
};
|
|
|
|
|
}
|