mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-08-28 12:56:57 +02:00
This commit is contained in:
parent
a4931d8e4f
commit
def7a1b210
17 changed files with 2209 additions and 1790 deletions
292
proc/src/lib.rs
292
proc/src/lib.rs
|
|
@ -5,18 +5,20 @@ use std::collections::{HashMap, BTreeMap};
|
|||
use syn::{
|
||||
Error, Path, Ident, Variant, Fields, BinOp,
|
||||
Expr, ExprPath, ExprBinary, ExprAssign, ExprLit, Lit, LitStr,
|
||||
ItemEnum, ItemImpl, ImplItem, ImplItemFn, Signature,
|
||||
MetaList, Type, TypePath, FnArg, PatType,
|
||||
Item, ItemEnum, ItemImpl, ImplItem, ImplItemFn, ItemTrait, TraitItem, TraitItemFn,
|
||||
Signature, MetaList, Type, TypePath, FnArg, PatType, Attribute,
|
||||
parse::{ParseStream, Parse, Result},
|
||||
spanned::Spanned
|
||||
spanned::Spanned,
|
||||
punctuated::Punctuated,
|
||||
token::Comma
|
||||
};
|
||||
|
||||
macro_rules! attribute {
|
||||
($name:ident { $($body:tt)* }) => {
|
||||
#[proc_macro_attribute] pub fn $name (meta: TokenStream, item: TokenStream) -> TokenStream {
|
||||
write(self::$name::Def(
|
||||
syn::parse_macro_input!(meta as self::$name::Meta),
|
||||
syn::parse_macro_input!(item as self::$name::Item),
|
||||
write(self::$name::CustomAttribute(
|
||||
syn::parse_macro_input!(meta as self::$name::CustomAttributeMeta),
|
||||
syn::parse_macro_input!(item as self::$name::CustomAttributeItem),
|
||||
))
|
||||
}
|
||||
mod $name {
|
||||
|
|
@ -27,70 +29,139 @@ macro_rules! attribute {
|
|||
}
|
||||
|
||||
attribute!(commands {
|
||||
#[derive(Debug, Clone)] pub struct Def(pub Meta, pub Item);
|
||||
#[derive(Debug, Clone)] pub struct Meta(pub Path, pub LitStr);
|
||||
#[derive(Debug, Clone)] pub struct Item(
|
||||
pub Path, pub ItemImpl, pub HashMap<Ident, (Ident, Vec<FnArg>, LitStr)>
|
||||
#[derive(Debug, Clone)] pub struct CustomAttribute(
|
||||
pub CustomAttributeMeta,
|
||||
pub CustomAttributeItem,
|
||||
);
|
||||
#[derive(Debug, Clone)] pub struct CustomAttributeMeta(
|
||||
pub Path,
|
||||
pub Option<LitStr>
|
||||
);
|
||||
#[derive(Debug, Clone)] pub struct CustomAttributeItem(
|
||||
pub Path,
|
||||
pub CustomAttributeItemDispatch,
|
||||
pub Item
|
||||
);
|
||||
|
||||
impl Parse for Meta {
|
||||
pub type CustomAttributeItemDispatch = HashMap<Ident, (Ident, Vec<FnArg>, LitStr)>;
|
||||
|
||||
impl Parse for CustomAttributeMeta {
|
||||
/// Parse contents of `#[command(...)]` attribute tag.
|
||||
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: Lit::Str(lit), .. }) = &**right
|
||||
{
|
||||
Ok(Self(path.clone(), lit.clone()))
|
||||
} else {
|
||||
Err(Error::new(meta.span(), format!(
|
||||
"must be: #[tek_proc::commands(Struct = \"struct\")], got: {meta:?}"
|
||||
let meta: Expr = input.parse()?;
|
||||
Ok(match meta {
|
||||
// Struct name only
|
||||
Expr::Path(ExprPath { path, .. }) => Self(path, None),
|
||||
|
||||
// Struct name with namespace prefix
|
||||
Expr::Assign(ExprAssign { ref left, ref right, .. })
|
||||
if let Expr::Path(ExprPath { path, .. }) = &**left
|
||||
&& let Expr::Lit(ExprLit { lit: Lit::Str(lit), .. }) = &**right
|
||||
=> Self(path.clone(), Some(lit.clone())),
|
||||
|
||||
// All other variants invalid
|
||||
_ => return Err(Error::new(meta.span(), format!(
|
||||
"must be: #[commands(Struct)] or #[commands(Struct = \"struct\")], got: {meta:?}"
|
||||
)))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Item {
|
||||
impl Parse for CustomAttributeItem {
|
||||
/// Parse contents of `trait` or `impl` block annotated with `#[command(...)]`
|
||||
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>, LitStr)> = 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: Lit::Str(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))
|
||||
let mut item: Item = input.parse()?;
|
||||
Ok(Self(
|
||||
parse_custom_attribute_item_path(&item)?,
|
||||
parse_custom_attribute_item_dispatch(&mut item)?,
|
||||
item,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Def {
|
||||
fn parse_custom_attribute_item_path (item: &Item) -> Result<Path> {
|
||||
Ok(match item {
|
||||
Item::Trait(ItemTrait { ident, .. }) => Path {
|
||||
leading_colon: None,
|
||||
segments: syn::punctuated::Punctuated::from_iter([
|
||||
syn::PathSegment {
|
||||
ident: ident.clone(),
|
||||
arguments: syn::PathArguments::None // TODO support generics
|
||||
}
|
||||
])
|
||||
},
|
||||
|
||||
Item::Impl(ItemImpl { self_ty, .. }) if let Type::Path(TypePath {
|
||||
path, ..
|
||||
}) = &**self_ty => path.clone(),
|
||||
|
||||
_ => return Err(
|
||||
Error::new(item.span(), format!("#[commands] works on trait or inherent impl"))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Pick out annotated functions from the `trait` or `impl` block,
|
||||
/// adding them to the [CustomAttributeItemDispatch] collection.
|
||||
fn parse_custom_attribute_item_dispatch (item: &mut Item)
|
||||
-> Result<CustomAttributeItemDispatch>
|
||||
{
|
||||
let mut dispatch: CustomAttributeItemDispatch = Default::default();
|
||||
let mut dispatch_attrs = |
|
||||
attrs: &Vec<Attribute>, ident: &Ident, inputs: &Punctuated<FnArg, Comma>
|
||||
| {
|
||||
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: Lit::Str(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()
|
||||
};
|
||||
|
||||
match item {
|
||||
Item::Trait(ItemTrait { items, .. }) => {
|
||||
for item in items.iter_mut() {
|
||||
if let TraitItem::Fn(TraitItemFn {
|
||||
attrs, sig: Signature { ident, inputs, .. }, ..
|
||||
}) = item {
|
||||
*attrs = dispatch_attrs(attrs, ident, inputs);
|
||||
}
|
||||
}
|
||||
},
|
||||
Item::Impl(ItemImpl { items, .. }) => {
|
||||
for item in items.iter_mut() {
|
||||
if let ImplItem::Fn(ImplItemFn {
|
||||
attrs, sig: Signature { ident, inputs, .. }, ..
|
||||
}) = item {
|
||||
*attrs = dispatch_attrs(attrs, ident, inputs);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => return Err(
|
||||
Error::new(item.span(), format!("#[commands] works on trait or inherent impl"))
|
||||
)
|
||||
}
|
||||
Ok(dispatch)
|
||||
}
|
||||
|
||||
impl ToTokens for CustomAttribute {
|
||||
fn to_tokens (&self, out: &mut TokenStream2) {
|
||||
let Self(Meta(command, namespace), Item(state, item, items)) = self;
|
||||
let Self(
|
||||
CustomAttributeMeta(command, namespace),
|
||||
CustomAttributeItem(state, items, item),
|
||||
) = self;
|
||||
let mut variants = quote! {};
|
||||
let mut dispatch = quote! {};
|
||||
let mut keywords = quote! {};
|
||||
|
|
@ -116,7 +187,11 @@ attribute!(commands {
|
|||
} else {
|
||||
quote! { #command::#variant => state.#ident(), }
|
||||
});
|
||||
let keyword = format!("{}/{}", namespace.value(), keyword.value());
|
||||
let keyword = if let Some(namespace) = namespace {
|
||||
format!("{}/{}", namespace.value(), keyword.value())
|
||||
} else {
|
||||
keyword.value()
|
||||
};
|
||||
if has_args {
|
||||
append(&mut expressions, quote! {
|
||||
#keyword (#typed) => { #command::#variant { #params } },
|
||||
|
|
@ -127,24 +202,40 @@ attribute!(commands {
|
|||
});
|
||||
}
|
||||
}
|
||||
append(out, quote! {
|
||||
#[derive(Debug, Clone)] pub enum #command { #variants }
|
||||
|
||||
impl<'a> dizzle::Namespace<'a, #command> for #state {
|
||||
symbols!('a |state| -> #command {
|
||||
#keywords
|
||||
});
|
||||
expressions!('a |state| -> #command {
|
||||
#expressions
|
||||
});
|
||||
}
|
||||
|
||||
impl #command {
|
||||
pub fn act (self, state: &mut #state) -> Perhaps<Self> {
|
||||
match self { #dispatch _ => unreachable!() }
|
||||
let impls = match item {
|
||||
Item::Impl(ItemImpl { generics, .. }) => {
|
||||
let lts = Punctuated::<_, Comma>::from_iter(generics.lifetimes());
|
||||
let tys = Punctuated::<_, Comma>::from_iter(generics.type_params());
|
||||
let cns = Punctuated::<_, Comma>::from_iter(generics.const_params());
|
||||
quote! {
|
||||
impl<'n, #tys> dizzle::Namespace<'n, #command> for #state {
|
||||
symbols!('n |state: Self| -> #command { #keywords });
|
||||
expressions!('n |state: Self| -> #command { #expressions });
|
||||
}
|
||||
impl #generics #command {
|
||||
pub fn act (self, state: &mut #state) -> Perhaps<Self> {
|
||||
match self { #dispatch _ => unreachable!() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
Item::Trait { .. } => quote! {
|
||||
impl<'n, T: #state + 'n> dizzle::Namespaced<'n, T> for #command {
|
||||
symbols!('n |state: T| -> Self { #keywords });
|
||||
expressions!('n |state: T| -> Self { #expressions });
|
||||
}
|
||||
impl #command {
|
||||
pub fn act <'n, T: #state + 'n> (self, state: &mut T) -> Perhaps<Self> {
|
||||
match self { #dispatch _ => unreachable!() }
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => panic!("trait or inherent impl needed for #[commands]")
|
||||
};
|
||||
append(out, quote! {
|
||||
/// Command variants
|
||||
#[derive(Debug, Clone)] pub enum #command { #variants }
|
||||
#impls
|
||||
#item
|
||||
})
|
||||
}
|
||||
|
|
@ -152,15 +243,22 @@ attribute!(commands {
|
|||
});
|
||||
|
||||
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 {
|
||||
#[derive(Debug, Clone)] pub struct CustomAttribute(
|
||||
pub CustomAttributeMeta,
|
||||
pub CustomAttributeItem
|
||||
);
|
||||
#[derive(Debug, Clone)] pub struct CustomAttributeMeta(
|
||||
pub Path
|
||||
);
|
||||
#[derive(Debug, Clone)] pub struct CustomAttributeItem(
|
||||
pub ItemEnum, pub HashMap<Ident, (Fields, Expr)>
|
||||
);
|
||||
impl Parse for CustomAttributeMeta {
|
||||
fn parse (input: ParseStream) -> Result<Self> {
|
||||
Ok(Self(input.parse()?))
|
||||
}
|
||||
}
|
||||
impl Parse for Item {
|
||||
impl Parse for CustomAttributeItem {
|
||||
fn parse (input: ParseStream) -> Result<Self> {
|
||||
let mut item: ItemEnum = input.parse()?;
|
||||
let mut branches: HashMap<Ident, (Fields, Expr)> = Default::default();
|
||||
|
|
@ -187,9 +285,12 @@ attribute!(command {
|
|||
Ok(Self(item, branches))
|
||||
}
|
||||
}
|
||||
impl ToTokens for Def {
|
||||
impl ToTokens for CustomAttribute {
|
||||
fn to_tokens (&self, out: &mut TokenStream2) {
|
||||
let Self(Meta(state), Item(item, branches)) = self;
|
||||
let Self(
|
||||
CustomAttributeMeta(state),
|
||||
CustomAttributeItem(item, branches)
|
||||
) = self;
|
||||
let ident = &item.ident;
|
||||
let mut body = quote! {};
|
||||
for (variant, (fields, handler)) in branches.iter() {
|
||||
|
|
@ -249,21 +350,21 @@ attribute!(command {
|
|||
});
|
||||
|
||||
attribute!(keyword {
|
||||
#[derive(Debug, Clone)] pub struct Def(
|
||||
pub Meta, pub Item
|
||||
#[derive(Debug, Clone)] pub struct CustomAttribute(
|
||||
pub CustomAttributeMeta, pub CustomAttributeItem
|
||||
);
|
||||
#[derive(Debug, Clone)] pub struct Meta(
|
||||
#[derive(Debug, Clone)] pub struct CustomAttributeMeta(
|
||||
pub Path
|
||||
);
|
||||
#[derive(Debug, Clone)] pub struct Item(
|
||||
#[derive(Debug, Clone)] pub struct CustomAttributeItem(
|
||||
pub ItemEnum, pub BTreeMap<Ident, Vec<(Literal, BTreeMap<Ident, Type>)>>
|
||||
);
|
||||
impl Parse for Meta {
|
||||
impl Parse for CustomAttributeMeta {
|
||||
fn parse (input: ParseStream) -> Result<Self> {
|
||||
Ok(Self(input.parse()?))
|
||||
}
|
||||
}
|
||||
impl Parse for Item {
|
||||
impl Parse for CustomAttributeItem {
|
||||
fn parse (input: ParseStream) -> Result<Self> {
|
||||
let mut item: ItemEnum = input.parse()?;
|
||||
for Variant { attrs, ident, fields, discriminant } in item.variants.iter_mut() {
|
||||
|
|
@ -281,18 +382,19 @@ attribute!(keyword {
|
|||
Ok(Self(item, Default::default()))
|
||||
}
|
||||
}
|
||||
impl ToTokens for Def {
|
||||
impl ToTokens for CustomAttribute {
|
||||
fn to_tokens (&self, out: &mut TokenStream2) {
|
||||
let Self(Meta(state), Item(item, variants)) = self;
|
||||
let Self(
|
||||
CustomAttributeMeta(state),
|
||||
CustomAttributeItem(item, variants)
|
||||
) = self;
|
||||
let ident = &item.ident;
|
||||
let body = quote! {};
|
||||
append(out, quote! {
|
||||
#item
|
||||
|
||||
impl<'a> Namespace<'a, #ident> for #state {
|
||||
symbols!('a |_state| -> #ident {
|
||||
#body,
|
||||
});
|
||||
impl<'n> Namespace<'n, #ident> for #state {
|
||||
symbols!('n |_state: #state| -> #ident { #body, });
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue