mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-08-28 12:56:57 +02:00
rework command/namespace dispatch
This commit is contained in:
parent
def7a1b210
commit
406aabde6e
15 changed files with 586 additions and 374 deletions
216
proc/src/lib.rs
216
proc/src/lib.rs
|
|
@ -48,7 +48,7 @@ attribute!(commands {
|
|||
impl Parse for CustomAttributeMeta {
|
||||
/// Parse contents of `#[command(...)]` attribute tag.
|
||||
fn parse (input: ParseStream) -> Result<Self> {
|
||||
let meta: Expr = input.parse()?;
|
||||
let meta: Expr = input.parse()?;
|
||||
Ok(match meta {
|
||||
// Struct name only
|
||||
Expr::Path(ExprPath { path, .. }) => Self(path, None),
|
||||
|
|
@ -167,69 +167,18 @@ attribute!(commands {
|
|||
let mut keywords = quote! {};
|
||||
let mut expressions = quote! {};
|
||||
for (ident, (variant, inputs, keyword)) in items.iter() {
|
||||
let mut typed = quote! {};
|
||||
let mut params = quote! {};
|
||||
let mut has_args = false;
|
||||
for arg in inputs.iter() {
|
||||
if let FnArg::Typed(PatType { pat, ty, .. }) = arg {
|
||||
has_args = true;
|
||||
append(&mut typed, quote! { #pat: #ty, });
|
||||
append(&mut params, 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 = 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 } },
|
||||
});
|
||||
} else {
|
||||
append(&mut keywords, quote! {
|
||||
#keyword => #command::#variant,
|
||||
});
|
||||
}
|
||||
write_variant(
|
||||
&mut expressions, &mut keywords, &mut variants, &mut dispatch,
|
||||
ident, command, variant, inputs, namespace, keyword,
|
||||
)
|
||||
}
|
||||
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!() }
|
||||
}
|
||||
}
|
||||
},
|
||||
Item::Impl(ItemImpl { generics, .. }) => write_for_struct(
|
||||
keywords, expressions, dispatch, state, command, generics,
|
||||
),
|
||||
Item::Trait { .. } => write_for_trait(
|
||||
keywords, expressions, dispatch, state, command,
|
||||
),
|
||||
_ => panic!("trait or inherent impl needed for #[commands]")
|
||||
};
|
||||
append(out, quote! {
|
||||
|
|
@ -240,6 +189,145 @@ attribute!(commands {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn write_variant (
|
||||
exps: &mut TokenStream2,
|
||||
syms: &mut TokenStream2,
|
||||
vars: &mut TokenStream2,
|
||||
disp: &mut TokenStream2,
|
||||
ident: &Ident,
|
||||
command: &Path,
|
||||
variant: &Ident,
|
||||
inputs: &[FnArg],
|
||||
namespace: &Option<LitStr>,
|
||||
keyword: &LitStr,
|
||||
) {
|
||||
let keyword = if let Some(namespace) = namespace {
|
||||
format!("{}/{}", namespace.value(), keyword.value())
|
||||
} else {
|
||||
keyword.value()
|
||||
};
|
||||
let mut typed = quote! {};
|
||||
let mut params = quote! {};
|
||||
let mut obtain = quote! {};
|
||||
let mut has_args = false;
|
||||
for arg in inputs.iter() {
|
||||
if let FnArg::Typed(PatType { pat, ty, .. }) = arg {
|
||||
has_args = true;
|
||||
append(&mut typed, quote! { #pat: #ty, });
|
||||
append(&mut params, quote! { #pat, });
|
||||
append(&mut obtain, quote! { let #pat: #ty = {
|
||||
let head = tail.head()?.unwrap_or_default();
|
||||
let tail = tail.tail()?.unwrap_or_default();
|
||||
match dizzle::Namespace::<#ty>::namespace(state, &head)? {
|
||||
Some(arg) => arg,
|
||||
None => return Err(format!("{}: arg \"{}\" ({}) got: {head} {tail}",
|
||||
#keyword, stringify!(#pat), stringify!(#ty),
|
||||
).into())
|
||||
}
|
||||
};});
|
||||
}
|
||||
}
|
||||
if has_args {
|
||||
append(vars, quote! { #variant { #typed }, });
|
||||
append(disp, quote! { #command::#variant { #params } => state.#ident(#params), });
|
||||
append(exps, quote! {
|
||||
let tail_base = tail;
|
||||
if head.src()? == Some(#keyword) {
|
||||
let tail = tail_base;
|
||||
#obtain
|
||||
return Ok(Some(#command::#variant { #params }))
|
||||
}
|
||||
});
|
||||
} else {
|
||||
append(vars, quote! { #variant, });
|
||||
append(disp, quote! { #command::#variant => state.#ident(), });
|
||||
append(syms, quote! {
|
||||
if word == #keyword { return Ok(Some(#command::#variant)); }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn write_for_struct (
|
||||
syms: TokenStream2,
|
||||
exps: TokenStream2,
|
||||
disp: TokenStream2,
|
||||
state: &Path,
|
||||
command: &Path,
|
||||
generics: &syn::Generics,
|
||||
) -> TokenStream2 {
|
||||
//let lts = Punctuated::<_, Comma>::from_iter(generics.lifetimes());
|
||||
//let cns = Punctuated::<_, Comma>::from_iter(generics.const_params());
|
||||
let tys = Punctuated::<_, Comma>::from_iter(generics.type_params());
|
||||
quote! {
|
||||
impl<#tys> dizzle::Namespace<#command> for #state {
|
||||
//def_namespace_symbols!('n |state: Self| -> #command {
|
||||
//#syms
|
||||
//});
|
||||
//def_namespace_exps!('n |state: Self| -> #command {
|
||||
//#exps
|
||||
//});
|
||||
fn namespace_symbol (&self, word: impl Symbol) -> Perhaps<#command> {
|
||||
if let Some(word) = word.word()? {
|
||||
#syms
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
fn namespace_expression (&self, expr: impl Expression) -> Perhaps<#command> {
|
||||
let state = self;
|
||||
if let Some(expr) = expr.expr()? {
|
||||
let head = expr.head()?;
|
||||
let tail = expr.tail()?;
|
||||
#exps
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
impl #generics dizzle::Dispatch<#state> for #command {
|
||||
fn dispatch (self, state: &mut #state) -> Perhaps<Self> {
|
||||
match self {
|
||||
#disp
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_for_trait (
|
||||
syms: TokenStream2,
|
||||
exps: TokenStream2,
|
||||
disp: TokenStream2,
|
||||
state: &Path,
|
||||
command: &Path,
|
||||
) -> TokenStream2 {
|
||||
quote! {
|
||||
impl<T: #state> dizzle::Namespaced<T> for #command {
|
||||
fn namespaced_symbol <L: Symbol> (state: &T, word: L) -> Perhaps<#command> {
|
||||
if let Some(word) = word.word()? {
|
||||
#syms
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
fn namespaced_expression <L: Expression> (state: &T, expr: L) -> Perhaps<#command> {
|
||||
if let Some(expr) = expr.expr()? {
|
||||
let head = expr.head()?;
|
||||
let tail = expr.tail()?;
|
||||
#exps
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
impl<T: #state> dizzle::Dispatch<T> for #command {
|
||||
fn dispatch (self, state: &mut T) -> Perhaps<Self> {
|
||||
match self {
|
||||
#disp
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
attribute!(command {
|
||||
|
|
@ -393,8 +481,8 @@ attribute!(keyword {
|
|||
append(out, quote! {
|
||||
#item
|
||||
|
||||
impl<'n> Namespace<'n, #ident> for #state {
|
||||
symbols!('n |_state: #state| -> #ident { #body, });
|
||||
impl Namespace<#ident> for #state {
|
||||
def_namespace_symbols!(|_state: #state| -> #ident { #body, });
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue