2025-01-15 03:52:32 +09:00
|
|
|
use std::{
|
|
|
|
|
fmt::{Debug, Display},
|
|
|
|
|
hash::Hash,
|
|
|
|
|
ops::Not,
|
|
|
|
|
sync::Arc,
|
|
|
|
|
};
|
2025-01-07 23:15:18 +09:00
|
|
|
|
2025-01-21 00:39:25 +09:00
|
|
|
use smol_str::SmolStr;
|
2025-01-04 02:49:04 +09:00
|
|
|
|
2025-01-21 04:31:03 +09:00
|
|
|
use crate::parameter::{Parameter, ParameterValue};
|
2025-01-07 23:15:18 +09:00
|
|
|
|
2025-01-15 03:52:32 +09:00
|
|
|
pub type ParamName = &'static str;
|
2025-01-05 13:00:06 +09:00
|
|
|
|
2025-01-15 03:52:32 +09:00
|
|
|
#[derive(Debug, Clone)]
|
2024-09-13 16:02:30 +09:00
|
|
|
pub enum Token {
|
|
|
|
|
/// Token used to represent a finished command (i.e. no more parameters required)
|
|
|
|
|
// todo: this is likely not the right way to represent this
|
|
|
|
|
Empty,
|
|
|
|
|
|
2025-01-07 23:15:18 +09:00
|
|
|
/// multi-token matching
|
2025-01-14 11:53:56 +09:00
|
|
|
/// todo: FullString tokens don't work properly in this (they don't get passed the rest of the input)
|
2025-01-07 23:15:18 +09:00
|
|
|
Any(Vec<Token>),
|
|
|
|
|
|
|
|
|
|
/// A bot-defined command / subcommand (usually) (eg. "member" in `pk;member MyName`)
|
2025-01-04 02:49:04 +09:00
|
|
|
Value(Vec<SmolStr>),
|
2024-09-13 16:02:30 +09:00
|
|
|
|
2025-01-15 03:52:32 +09:00
|
|
|
/// A parameter that must be provided a value
|
|
|
|
|
Parameter(ParamName, Arc<dyn Parameter>),
|
|
|
|
|
}
|
2025-01-05 02:21:23 +09:00
|
|
|
|
2025-01-15 03:52:32 +09:00
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! any {
|
|
|
|
|
($($t:expr),+) => {
|
2025-01-21 04:31:03 +09:00
|
|
|
$crate::token::Token::Any(vec![$($crate::token::Token::from($t)),+])
|
2025-01-15 03:52:32 +09:00
|
|
|
};
|
|
|
|
|
}
|
2024-09-13 16:02:30 +09:00
|
|
|
|
2025-01-15 03:52:32 +09:00
|
|
|
impl PartialEq for Token {
|
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
|
match (self, other) {
|
|
|
|
|
(Self::Any(l0), Self::Any(r0)) => l0 == r0,
|
|
|
|
|
(Self::Value(l0), Self::Value(r0)) => l0 == r0,
|
|
|
|
|
(Self::Parameter(l0, _), Self::Parameter(r0, _)) => l0 == r0,
|
|
|
|
|
(Self::Empty, Self::Empty) => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl Eq for Token {}
|
2025-01-07 23:15:18 +09:00
|
|
|
|
2025-01-15 03:52:32 +09:00
|
|
|
impl Hash for Token {
|
|
|
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
|
|
|
core::mem::discriminant(self).hash(state);
|
|
|
|
|
match self {
|
|
|
|
|
Token::Empty => {}
|
|
|
|
|
Token::Any(vec) => vec.hash(state),
|
|
|
|
|
Token::Value(vec) => vec.hash(state),
|
|
|
|
|
Token::Parameter(name, _) => name.hash(state),
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-09-13 16:02:30 +09:00
|
|
|
}
|
|
|
|
|
|
2025-01-07 23:15:18 +09:00
|
|
|
#[derive(Debug)]
|
2025-01-12 04:23:46 +09:00
|
|
|
pub enum TokenMatchError {
|
2025-01-15 03:52:32 +09:00
|
|
|
ParameterMatchError { input: SmolStr, msg: SmolStr },
|
2025-01-07 23:15:18 +09:00
|
|
|
MissingParameter { name: ParamName },
|
2025-01-12 05:35:04 +09:00
|
|
|
MissingAny { tokens: Vec<Token> },
|
2025-01-07 23:15:18 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2025-01-21 04:31:03 +09:00
|
|
|
pub(super) struct TokenMatchValue {
|
2025-01-07 23:15:18 +09:00
|
|
|
pub raw: SmolStr,
|
2025-01-21 04:31:03 +09:00
|
|
|
pub param: Option<(ParamName, ParameterValue)>,
|
2024-09-13 16:02:30 +09:00
|
|
|
}
|
|
|
|
|
|
2025-01-14 11:53:56 +09:00
|
|
|
impl TokenMatchValue {
|
2025-01-12 04:23:46 +09:00
|
|
|
fn new_match(raw: impl Into<SmolStr>) -> TryMatchResult {
|
|
|
|
|
Some(Ok(Some(Self {
|
2025-01-07 23:15:18 +09:00
|
|
|
raw: raw.into(),
|
|
|
|
|
param: None,
|
2025-01-12 04:23:46 +09:00
|
|
|
})))
|
2025-01-07 23:15:18 +09:00
|
|
|
}
|
|
|
|
|
|
2025-01-12 04:23:46 +09:00
|
|
|
fn new_match_param(
|
|
|
|
|
raw: impl Into<SmolStr>,
|
|
|
|
|
param_name: ParamName,
|
2025-01-21 04:31:03 +09:00
|
|
|
param: ParameterValue,
|
2025-01-12 04:23:46 +09:00
|
|
|
) -> TryMatchResult {
|
|
|
|
|
Some(Ok(Some(Self {
|
2025-01-07 23:15:18 +09:00
|
|
|
raw: raw.into(),
|
|
|
|
|
param: Some((param_name, param)),
|
2025-01-12 04:23:46 +09:00
|
|
|
})))
|
2025-01-07 23:15:18 +09:00
|
|
|
}
|
|
|
|
|
}
|
2024-09-13 16:02:30 +09:00
|
|
|
|
2025-01-12 04:23:46 +09:00
|
|
|
/// None -> no match
|
|
|
|
|
/// Some(Ok(None)) -> match, no value
|
|
|
|
|
/// Some(Ok(Some(_))) -> match, with value
|
|
|
|
|
/// Some(Err(_)) -> error while matching
|
|
|
|
|
// q: why do this while we could have a NoMatch in TokenMatchError?
|
|
|
|
|
// a: because we want to differentiate between no match and match failure (it matched with an error)
|
|
|
|
|
// "no match" has a different charecteristic because we want to continue matching other tokens...
|
|
|
|
|
// ...while "match failure" means we should stop matching and return the error
|
2025-01-14 11:53:56 +09:00
|
|
|
type TryMatchResult = Option<Result<Option<TokenMatchValue>, TokenMatchError>>;
|
2024-09-13 16:02:30 +09:00
|
|
|
|
2025-01-12 04:23:46 +09:00
|
|
|
impl Token {
|
2025-01-21 04:31:03 +09:00
|
|
|
pub(super) fn try_match(&self, input: Option<&str>) -> TryMatchResult {
|
2025-01-07 23:15:18 +09:00
|
|
|
let input = match input {
|
|
|
|
|
Some(input) => input,
|
|
|
|
|
None => {
|
|
|
|
|
// short circuit on:
|
|
|
|
|
return match self {
|
|
|
|
|
// empty token
|
2025-01-12 04:23:46 +09:00
|
|
|
Self::Empty => Some(Ok(None)),
|
2025-01-07 23:15:18 +09:00
|
|
|
// missing paramaters
|
2025-01-15 03:52:32 +09:00
|
|
|
Self::Parameter(name, _) => {
|
|
|
|
|
Some(Err(TokenMatchError::MissingParameter { name }))
|
2025-01-08 18:31:59 +09:00
|
|
|
}
|
2025-01-12 04:23:46 +09:00
|
|
|
Self::Any(tokens) => tokens.is_empty().then_some(None).unwrap_or_else(|| {
|
2025-01-12 05:35:04 +09:00
|
|
|
Some(Err(TokenMatchError::MissingAny {
|
|
|
|
|
tokens: tokens.clone(),
|
|
|
|
|
}))
|
2025-01-12 04:23:46 +09:00
|
|
|
}),
|
2025-01-07 23:15:18 +09:00
|
|
|
// everything else doesnt match if no input anyway
|
2025-01-14 11:53:56 +09:00
|
|
|
Self::Value(_) => None,
|
2025-01-07 23:15:18 +09:00
|
|
|
// don't add a _ match here!
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let input = input.trim();
|
2024-09-13 16:02:30 +09:00
|
|
|
|
|
|
|
|
// try actually matching stuff
|
|
|
|
|
match self {
|
2025-01-12 04:23:46 +09:00
|
|
|
Self::Empty => None,
|
2025-01-07 23:15:18 +09:00
|
|
|
Self::Any(tokens) => tokens
|
|
|
|
|
.iter()
|
2025-01-14 11:53:56 +09:00
|
|
|
.map(|t| t.try_match(Some(input)))
|
2025-01-12 04:23:46 +09:00
|
|
|
.find(|r| !matches!(r, None))
|
|
|
|
|
.unwrap_or(None),
|
2025-01-07 23:15:18 +09:00
|
|
|
Self::Value(values) => values
|
|
|
|
|
.iter()
|
|
|
|
|
.any(|v| v.eq(input))
|
2025-01-14 11:53:56 +09:00
|
|
|
.then(|| TokenMatchValue::new_match(input))
|
2025-01-12 04:23:46 +09:00
|
|
|
.unwrap_or(None),
|
2025-01-15 03:52:32 +09:00
|
|
|
Self::Parameter(name, param) => match param.match_value(input) {
|
|
|
|
|
Ok(matched) => TokenMatchValue::new_match_param(input, name, matched),
|
|
|
|
|
Err(err) => Some(Err(TokenMatchError::ParameterMatchError {
|
|
|
|
|
input: input.into(),
|
|
|
|
|
msg: err,
|
|
|
|
|
})),
|
|
|
|
|
}, // don't add a _ match here!
|
2025-01-07 23:15:18 +09:00
|
|
|
}
|
2024-09-13 16:02:30 +09:00
|
|
|
}
|
|
|
|
|
}
|
2025-01-05 00:59:59 +09:00
|
|
|
|
2025-01-08 18:31:59 +09:00
|
|
|
impl Display for Token {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
Token::Empty => write!(f, ""),
|
|
|
|
|
Token::Any(vec) => {
|
|
|
|
|
write!(f, "(")?;
|
|
|
|
|
for (i, token) in vec.iter().enumerate() {
|
|
|
|
|
if i != 0 {
|
2025-01-14 11:53:56 +09:00
|
|
|
write!(f, "|")?;
|
2025-01-08 18:31:59 +09:00
|
|
|
}
|
|
|
|
|
write!(f, "{}", token)?;
|
|
|
|
|
}
|
|
|
|
|
write!(f, ")")
|
|
|
|
|
}
|
|
|
|
|
Token::Value(vec) if vec.is_empty().not() => write!(f, "{}", vec.first().unwrap()),
|
|
|
|
|
Token::Value(_) => Ok(()), // if value token has no values (lol), don't print anything
|
2025-01-15 03:52:32 +09:00
|
|
|
Token::Parameter(name, param) => param.format(f, name),
|
2025-01-08 18:31:59 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-14 11:53:56 +09:00
|
|
|
impl From<&str> for Token {
|
|
|
|
|
fn from(value: &str) -> Self {
|
2025-01-21 00:39:25 +09:00
|
|
|
Token::Value(vec![SmolStr::new(value)])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<const L: usize> From<[&str; L]> for Token {
|
|
|
|
|
fn from(value: [&str; L]) -> Self {
|
|
|
|
|
Token::Value(value.into_iter().map(SmolStr::from).collect::<Vec<_>>())
|
2025-01-05 00:59:59 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-15 03:52:32 +09:00
|
|
|
impl<P: Parameter + 'static> From<P> for Token {
|
|
|
|
|
fn from(value: P) -> Self {
|
|
|
|
|
Token::Parameter(value.default_name(), Arc::new(value))
|
2025-01-08 18:31:59 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-15 03:52:32 +09:00
|
|
|
impl<P: Parameter + 'static> From<(ParamName, P)> for Token {
|
|
|
|
|
fn from(value: (ParamName, P)) -> Self {
|
|
|
|
|
Token::Parameter(value.0, Arc::new(value.1))
|
2025-01-08 18:31:59 +09:00
|
|
|
}
|
|
|
|
|
}
|