feat(commands): add cs codegen to statically use params and flags in bot code, remove Any

This commit is contained in:
dusk 2025-01-21 12:36:54 +09:00
parent 0c012e98b5
commit 07e8a4851a
No known key found for this signature in database
20 changed files with 297 additions and 417 deletions

View file

@ -1,76 +1,35 @@
use std::{
fmt::{Debug, Display},
hash::Hash,
ops::Not,
sync::Arc,
};
use smol_str::SmolStr;
use crate::parameter::{Parameter, ParameterValue};
use crate::parameter::{Parameter, ParameterKind, ParameterValue};
pub type ParamName = &'static str;
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
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,
/// multi-token matching
/// todo: FullString tokens don't work properly in this (they don't get passed the rest of the input)
Any(Vec<Token>),
/// A bot-defined command / subcommand (usually) (eg. "member" in `pk;member MyName`)
Value(Vec<SmolStr>),
/// A parameter that must be provided a value
Parameter(ParamName, Arc<dyn Parameter>),
}
#[macro_export]
macro_rules! any {
($($t:expr),+) => {
$crate::token::Token::Any(vec![$($crate::token::Token::from($t)),+])
};
}
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 {}
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),
}
}
Parameter(Parameter),
}
#[derive(Debug)]
pub enum TokenMatchError {
ParameterMatchError { input: SmolStr, msg: SmolStr },
MissingParameter { name: ParamName },
MissingAny { tokens: Vec<Token> },
MissingParameter { name: SmolStr },
}
#[derive(Debug)]
pub(super) struct TokenMatchValue {
pub raw: SmolStr,
pub param: Option<(ParamName, ParameterValue)>,
pub param: Option<(SmolStr, ParameterValue)>,
}
impl TokenMatchValue {
@ -83,12 +42,12 @@ impl TokenMatchValue {
fn new_match_param(
raw: impl Into<SmolStr>,
param_name: ParamName,
param_name: impl Into<SmolStr>,
param: ParameterValue,
) -> TryMatchResult {
Some(Ok(Some(Self {
raw: raw.into(),
param: Some((param_name, param)),
param: Some((param_name.into(), param)),
})))
}
}
@ -113,14 +72,9 @@ impl Token {
// empty token
Self::Empty => Some(Ok(None)),
// missing paramaters
Self::Parameter(name, _) => {
Some(Err(TokenMatchError::MissingParameter { name }))
}
Self::Any(tokens) => tokens.is_empty().then_some(None).unwrap_or_else(|| {
Some(Err(TokenMatchError::MissingAny {
tokens: tokens.clone(),
}))
}),
Self::Parameter(param) => Some(Err(TokenMatchError::MissingParameter {
name: param.name().into(),
})),
// everything else doesnt match if no input anyway
Self::Value(_) => None,
// don't add a _ match here!
@ -132,18 +86,13 @@ impl Token {
// try actually matching stuff
match self {
Self::Empty => None,
Self::Any(tokens) => tokens
.iter()
.map(|t| t.try_match(Some(input)))
.find(|r| !matches!(r, None))
.unwrap_or(None),
Self::Value(values) => values
.iter()
.any(|v| v.eq(input))
.then(|| TokenMatchValue::new_match(input))
.unwrap_or(None),
Self::Parameter(name, param) => match param.match_value(input) {
Ok(matched) => TokenMatchValue::new_match_param(input, name, matched),
Self::Parameter(param) => match param.kind().match_value(input) {
Ok(matched) => TokenMatchValue::new_match_param(input, param.name(), matched),
Err(err) => Some(Err(TokenMatchError::ParameterMatchError {
input: input.into(),
msg: err,
@ -157,19 +106,9 @@ 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 {
write!(f, "|")?;
}
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
Token::Parameter(name, param) => param.format(f, name),
Token::Parameter(param) => param.kind().format(f, param.name()),
}
}
}
@ -186,14 +125,20 @@ impl<const L: usize> From<[&str; L]> for Token {
}
}
impl<P: Parameter + 'static> From<P> for Token {
fn from(value: P) -> Self {
Token::Parameter(value.default_name(), Arc::new(value))
impl From<Parameter> for Token {
fn from(value: Parameter) -> Self {
Token::Parameter(value)
}
}
impl<P: Parameter + 'static> From<(ParamName, P)> for Token {
fn from(value: (ParamName, P)) -> Self {
Token::Parameter(value.0, Arc::new(value.1))
impl From<ParameterKind> for Token {
fn from(value: ParameterKind) -> Self {
Token::from(Parameter::from(value))
}
}
impl From<(&str, ParameterKind)> for Token {
fn from(value: (&str, ParameterKind)) -> Self {
Token::from(Parameter::from(value))
}
}