wip support for expressions in #[commands] procmacro
Some checks are pending
/ build (push) Waiting to run

This commit is contained in:
i do not exist 2026-08-10 17:24:57 +03:00
parent 62f30daac6
commit a4931d8e4f

View file

@ -3,8 +3,8 @@ use proc_macro2::{TokenStream as TokenStream2, Span};
use quote::{quote, ToTokens, TokenStreamExt}; use quote::{quote, ToTokens, TokenStreamExt};
use std::collections::{HashMap, BTreeMap}; use std::collections::{HashMap, BTreeMap};
use syn::{ use syn::{
Error, Path, Lit, Ident, Variant, Fields, BinOp, Error, Path, Ident, Variant, Fields, BinOp,
Expr, ExprPath, ExprBinary, ExprAssign, ExprLit, Expr, ExprPath, ExprBinary, ExprAssign, ExprLit, Lit, LitStr,
ItemEnum, ItemImpl, ImplItem, ImplItemFn, Signature, ItemEnum, ItemImpl, ImplItem, ImplItemFn, Signature,
MetaList, Type, TypePath, FnArg, PatType, MetaList, Type, TypePath, FnArg, PatType,
parse::{ParseStream, Parse, Result}, parse::{ParseStream, Parse, Result},
@ -28,9 +28,9 @@ macro_rules! attribute {
attribute!(commands { attribute!(commands {
#[derive(Debug, Clone)] pub struct Def(pub Meta, pub Item); #[derive(Debug, Clone)] pub struct Def(pub Meta, pub Item);
#[derive(Debug, Clone)] pub struct Meta(pub Path, pub Lit); #[derive(Debug, Clone)] pub struct Meta(pub Path, pub LitStr);
#[derive(Debug, Clone)] pub struct Item( #[derive(Debug, Clone)] pub struct Item(
pub Path, pub ItemImpl, pub HashMap<Ident, (Ident, Vec<FnArg>, Lit)> pub Path, pub ItemImpl, pub HashMap<Ident, (Ident, Vec<FnArg>, LitStr)>
); );
impl Parse for Meta { impl Parse for Meta {
@ -38,7 +38,7 @@ attribute!(commands {
let meta = input.parse()?; let meta = input.parse()?;
if let Expr::Assign(ExprAssign { ref left, ref right, .. }) = meta if let Expr::Assign(ExprAssign { ref left, ref right, .. }) = meta
&& let Expr::Path(ExprPath { path, .. }) = &**left && let Expr::Path(ExprPath { path, .. }) = &**left
&& let Expr::Lit(ExprLit { lit, .. }) = &**right && let Expr::Lit(ExprLit { lit: Lit::Str(lit), .. }) = &**right
{ {
Ok(Self(path.clone(), lit.clone())) Ok(Self(path.clone(), lit.clone()))
} else { } else {
@ -57,7 +57,7 @@ attribute!(commands {
} else { } else {
return Err(Error::new(item.self_ty.span(), format!("must be path to struct"))) return Err(Error::new(item.self_ty.span(), format!("must be path to struct")))
}; };
let mut dispatch: HashMap<Ident, (Ident, Vec<FnArg>, Lit)> = Default::default(); let mut dispatch: HashMap<Ident, (Ident, Vec<FnArg>, LitStr)> = Default::default();
for item in item.items.iter_mut() { for item in item.items.iter_mut() {
if let ImplItem::Fn(ImplItemFn { if let ImplItem::Fn(ImplItemFn {
attrs, sig: Signature { ident, inputs, .. }, .. attrs, sig: Signature { ident, inputs, .. }, ..
@ -67,7 +67,7 @@ attribute!(commands {
&& path == &Path::from(Ident::new("command", Span::call_site())) && path == &Path::from(Ident::new("command", Span::call_site()))
&& let Ok(handler) = syn::parse2::<Expr>(tokens.clone()) && let Ok(handler) = syn::parse2::<Expr>(tokens.clone())
&& let Expr::Assign(ExprAssign { ref left, ref right, .. }) = handler && let Expr::Assign(ExprAssign { ref left, ref right, .. }) = handler
&& let Expr::Lit(ExprLit { lit, .. }) = &**right && let Expr::Lit(ExprLit { lit: Lit::Str(lit), .. }) = &**right
&& let Expr::Path(ExprPath { path, .. }) = &**left && let Expr::Path(ExprPath { path, .. }) = &**left
&& path.segments.len() == 1 { && path.segments.len() == 1 {
dispatch.insert(ident.clone(), ( dispatch.insert(ident.clone(), (
@ -90,41 +90,62 @@ attribute!(commands {
impl ToTokens for Def { impl ToTokens for Def {
fn to_tokens (&self, out: &mut TokenStream2) { fn to_tokens (&self, out: &mut TokenStream2) {
let Self(Meta(command, namespace), Item(ident, item, items)) = self; let Self(Meta(command, namespace), Item(state, item, items)) = self;
let mut variants = quote! {}; let mut variants = quote! {};
let mut dispatch = quote! {}; let mut dispatch = quote! {};
let mut keywords = quote! {};
let mut expressions = quote! {};
for (ident, (variant, inputs, keyword)) in items.iter() { for (ident, (variant, inputs, keyword)) in items.iter() {
let mut typed = quote! {};
let mut params = quote! {}; let mut params = quote! {};
let mut args = quote! {};
let mut has_args = false; let mut has_args = false;
for arg in inputs.iter() { for arg in inputs.iter() {
match arg { if let FnArg::Typed(PatType { pat, ty, .. }) = arg {
FnArg::Receiver(_) => {}, has_args = true;
FnArg::Typed(PatType { pat, ty, .. }) => { append(&mut typed, quote! { #pat: #ty, });
has_args = true; append(&mut params, quote! { #pat, });
append(&mut params, quote! { #pat: #ty, });
append(&mut args, quote! { #pat, })
}
} }
} }
append(&mut variants, if has_args {
quote! { #variant { #typed }, }
} else {
quote! { #variant, }
});
append(&mut dispatch, if has_args {
quote! { #command::#variant { #params } => state.#ident(#params), }
} else {
quote! { #command::#variant => state.#ident(), }
});
let keyword = format!("{}/{}", namespace.value(), keyword.value());
if has_args { if has_args {
params = quote! { { #params } }; append(&mut expressions, quote! {
args = quote! { { #args } }; #keyword (#typed) => { #command::#variant { #params } },
});
} else {
append(&mut keywords, quote! {
#keyword => #command::#variant,
});
} }
append(&mut variants, quote! { #variant #params, });
append(&mut dispatch, quote! { #command::#variant #args => { todo!() }, });
} }
append(out, quote! { append(out, quote! {
#item #[derive(Debug, Clone)] pub enum #command { #variants }
pub enum #command { #variants }
impl dizzle::Act<#ident> for #command { impl<'a> dizzle::Namespace<'a, #command> for #state {
fn act (&self, state: &mut #ident) -> Perhaps<Self> { symbols!('a |state| -> #command {
match self { #keywords
#dispatch });
_ => unreachable!() expressions!('a |state| -> #command {
} #expressions
});
}
impl #command {
pub fn act (self, state: &mut #state) -> Perhaps<Self> {
match self { #dispatch _ => unreachable!() }
} }
} }
#item
}) })
} }
} }
@ -269,7 +290,7 @@ attribute!(keyword {
#item #item
impl<'a> Namespace<'a, #ident> for #state { impl<'a> Namespace<'a, #ident> for #state {
symbols!('a |stte| -> #ident { symbols!('a |_state| -> #ident {
#body, #body,
}); });
} }