mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-08-28 12:56:57 +02:00
This commit is contained in:
parent
ae496987c8
commit
7fcab73b04
12 changed files with 811 additions and 559 deletions
122
proc/src/lib.rs
122
proc/src/lib.rs
|
|
@ -3,8 +3,12 @@ use proc_macro2::{TokenStream as TokenStream2, Span};
|
|||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use std::collections::{HashMap, BTreeMap};
|
||||
use syn::{
|
||||
Path, ItemEnum, Ident, Type, Variant, MetaList, Expr, ExprPath, ExprBinary, Fields, BinOp,
|
||||
parse::{ParseStream, Parse, Result}
|
||||
Error, Path, Lit, Ident, Variant, Fields, BinOp,
|
||||
Expr, ExprPath, ExprBinary, ExprAssign, ExprLit,
|
||||
ItemEnum, ItemImpl, ImplItem, ImplItemFn, Signature,
|
||||
MetaList, Type, TypePath, FnArg, PatType,
|
||||
parse::{ParseStream, Parse, Result},
|
||||
spanned::Spanned
|
||||
};
|
||||
|
||||
macro_rules! attribute {
|
||||
|
|
@ -22,16 +26,114 @@ macro_rules! attribute {
|
|||
}
|
||||
}
|
||||
|
||||
attribute!(command {
|
||||
#[derive(Debug, Clone)] pub struct Def(
|
||||
pub Meta, pub Item
|
||||
);
|
||||
#[derive(Debug, Clone)] pub struct Meta(
|
||||
pub Path
|
||||
);
|
||||
attribute!(commands {
|
||||
#[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 Item(
|
||||
pub ItemEnum, pub HashMap<Ident, (Fields, Expr)>
|
||||
pub Path, pub ItemImpl, pub HashMap<Ident, (Ident, Vec<FnArg>, Lit)>
|
||||
);
|
||||
|
||||
impl Parse for Meta {
|
||||
fn parse (input: ParseStream) -> Result<Self> {
|
||||
let meta = input.parse()?;
|
||||
if let Expr::Assign(ExprAssign { ref left, ref right, .. }) = meta
|
||||
&& let Expr::Path(ExprPath { path, .. }) = &**left
|
||||
&& let Expr::Lit(ExprLit { lit, .. }) = &**right
|
||||
{
|
||||
Ok(Self(path.clone(), lit.clone()))
|
||||
} else {
|
||||
Err(Error::new(meta.span(), format!(
|
||||
"must be: #[tek_proc::commands(Struct = \"struct\")], got: {meta:?}"
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Item {
|
||||
fn parse (input: ParseStream) -> Result<Self> {
|
||||
let mut item: ItemImpl = input.parse()?;
|
||||
let path = if let Type::Path(TypePath { path, .. }) = &*item.self_ty {
|
||||
path
|
||||
} else {
|
||||
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();
|
||||
for item in item.items.iter_mut() {
|
||||
if let ImplItem::Fn(ImplItemFn {
|
||||
attrs, sig: Signature { ident, inputs, .. }, ..
|
||||
}) = item {
|
||||
*attrs = attrs.iter().filter(|attr|{
|
||||
if let syn::Meta::List(MetaList { ref path, ref tokens, .. }) = attr.meta
|
||||
&& path == &Path::from(Ident::new("command", Span::call_site()))
|
||||
&& let Ok(handler) = syn::parse2::<Expr>(tokens.clone())
|
||||
&& let Expr::Assign(ExprAssign { ref left, ref right, .. }) = handler
|
||||
&& let Expr::Lit(ExprLit { lit, .. }) = &**right
|
||||
&& let Expr::Path(ExprPath { path, .. }) = &**left
|
||||
&& path.segments.len() == 1 {
|
||||
dispatch.insert(ident.clone(), (
|
||||
path.segments.first().unwrap().ident.clone(),
|
||||
inputs.iter().cloned().collect(),
|
||||
lit.clone()
|
||||
));
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
Ok(Self(path.clone(), item, dispatch))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Def {
|
||||
fn to_tokens (&self, out: &mut TokenStream2) {
|
||||
let Self(Meta(command, namespace), Item(ident, item, items)) = self;
|
||||
let mut variants = quote! {};
|
||||
let mut dispatch = quote! {};
|
||||
for (ident, (variant, inputs, keyword)) in items.iter() {
|
||||
let mut params = quote! {};
|
||||
let mut args = quote! {};
|
||||
let mut has_args = false;
|
||||
for arg in inputs.iter() {
|
||||
match arg {
|
||||
FnArg::Receiver(_) => {},
|
||||
FnArg::Typed(PatType { pat, ty, .. }) => {
|
||||
has_args = true;
|
||||
append(&mut params, quote! { #pat: #ty, });
|
||||
append(&mut args, quote! { #pat, })
|
||||
}
|
||||
}
|
||||
}
|
||||
if has_args {
|
||||
params = quote! { { #params } };
|
||||
args = quote! { { #args } };
|
||||
}
|
||||
append(&mut variants, quote! { #variant #params, });
|
||||
append(&mut dispatch, quote! { #command::#variant #args => { todo!() }, });
|
||||
}
|
||||
append(out, quote! {
|
||||
#item
|
||||
pub enum #command { #variants }
|
||||
impl dizzle::Act<#ident> for #command {
|
||||
fn act (&self, state: &mut #ident) -> Perhaps<Self> {
|
||||
match self {
|
||||
#dispatch
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
attribute!(command {
|
||||
#[derive(Debug, Clone)] pub struct Def(pub Meta, pub Item);
|
||||
#[derive(Debug, Clone)] pub struct Meta(pub Path);
|
||||
#[derive(Debug, Clone)] pub struct Item(pub ItemEnum, pub HashMap<Ident, (Fields, Expr)>);
|
||||
impl Parse for Meta {
|
||||
fn parse (input: ParseStream) -> Result<Self> {
|
||||
Ok(Self(input.parse()?))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue