RE ADD CONTEXT
This commit is contained in:
128
macro/src/lib.rs
128
macro/src/lib.rs
@@ -2,15 +2,19 @@ extern crate proc_macro;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
Attribute, Block, Ident, ItemTrait, Signature, Token, Visibility,
|
||||
Attribute, Block, Error, GenericParam, Generics, Ident, ItemStruct, ItemTrait, Meta, Signature,
|
||||
Token, Visibility,
|
||||
parse::{Parse, ParseStream, Result},
|
||||
parse_macro_input, parse_quote,
|
||||
punctuated::Punctuated,
|
||||
spanned::Spanned,
|
||||
};
|
||||
|
||||
struct Input {
|
||||
attrs: Vec<Attribute>,
|
||||
vis: Visibility,
|
||||
name: Ident,
|
||||
generics: Generics,
|
||||
fns: Vec<InputFn>,
|
||||
}
|
||||
|
||||
@@ -25,6 +29,7 @@ impl Parse for Input {
|
||||
let vis = input.parse()?;
|
||||
input.parse::<Token![trait]>()?;
|
||||
let name = input.parse()?;
|
||||
let generics = input.parse::<Generics>()?;
|
||||
input.parse::<Token![;]>()?;
|
||||
let mut fns = Vec::new();
|
||||
while !input.is_empty() {
|
||||
@@ -39,6 +44,7 @@ impl Parse for Input {
|
||||
attrs,
|
||||
vis,
|
||||
name,
|
||||
generics,
|
||||
fns,
|
||||
})
|
||||
}
|
||||
@@ -50,6 +56,7 @@ pub fn widget_trait(input: TokenStream) -> TokenStream {
|
||||
attrs,
|
||||
vis,
|
||||
name,
|
||||
mut generics,
|
||||
fns,
|
||||
} = parse_macro_input!(input as Input);
|
||||
|
||||
@@ -59,19 +66,130 @@ pub fn widget_trait(input: TokenStream) -> TokenStream {
|
||||
.map(|InputFn { sig, body }| quote! { #sig #body })
|
||||
.collect();
|
||||
|
||||
let Some(GenericParam::Type(state)) = generics.params.first() else {
|
||||
return Error::new(name.span(), "expected state generic parameter")
|
||||
.into_compile_error()
|
||||
.into();
|
||||
};
|
||||
|
||||
let state = &state.ident;
|
||||
|
||||
generics
|
||||
.params
|
||||
.push(parse_quote!(WL: WidgetLike<#state, Tag>));
|
||||
generics.params.push(parse_quote!(Tag));
|
||||
|
||||
let mut trai: ItemTrait = parse_quote!(
|
||||
#vis trait #name<WL: WidgetLike<Tag>, Tag> {
|
||||
#vis trait #name #generics {
|
||||
#(#sigs;)*
|
||||
}
|
||||
);
|
||||
|
||||
trai.attrs = attrs;
|
||||
|
||||
TokenStream::from(quote! {
|
||||
quote! {
|
||||
#trai
|
||||
|
||||
impl<WL: WidgetLike<Tag>, Tag> #name<WL, Tag> for WL {
|
||||
impl #generics #name<State, WL, Tag> for WL {
|
||||
#(#impls)*
|
||||
}
|
||||
})
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(GlobalState, attributes(has))]
|
||||
pub fn derive_global_state(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as ItemStruct);
|
||||
let name = input.ident;
|
||||
let mut impls = TokenStream::new();
|
||||
for field in input.fields {
|
||||
let Some(attr) = field.attrs.iter().find(|a| a.path().is_ident("has")) else {
|
||||
continue;
|
||||
};
|
||||
let error: TokenStream = Error::new(
|
||||
attr.span(),
|
||||
"invalid attribute format; usage: #[has(HasTrait, trait_fn)]",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into();
|
||||
let Meta::List(list) = &attr.meta else {
|
||||
return error;
|
||||
};
|
||||
match list.parse_args_with(Punctuated::<Ident, Token![,]>::parse_terminated) {
|
||||
Ok(list) => {
|
||||
if list.len() != 2 {
|
||||
return error;
|
||||
}
|
||||
let traitt = &list[0];
|
||||
let fn_name = &list[1];
|
||||
let field_name = field.ident;
|
||||
let ty = field.ty;
|
||||
impls.extend::<TokenStream>(
|
||||
quote! {
|
||||
impl #traitt for #name {
|
||||
fn #fn_name(&mut self) -> &mut #ty {
|
||||
&mut self.#field_name
|
||||
}
|
||||
}
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
Err(..) => {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
impls
|
||||
}
|
||||
|
||||
#[proc_macro_derive(HasUi)]
|
||||
pub fn derive_has_ui(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as ItemStruct);
|
||||
let name = input.ident;
|
||||
let Some(field) = input.fields.iter().find(|f| f.ty == parse_quote!(Ui<Self>)) else {
|
||||
return Error::new(name.span(), "could not find a Ui<Self> field for HasUi")
|
||||
.into_compile_error()
|
||||
.into();
|
||||
};
|
||||
let field = &field.ident;
|
||||
quote! {
|
||||
impl HasUi for #name {
|
||||
fn ui_ref(&self) -> &Ui<Self> {
|
||||
&self.#field
|
||||
}
|
||||
|
||||
fn ui(&mut self) -> &mut Ui<Self> {
|
||||
&mut self.#field
|
||||
}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(HasUiState)]
|
||||
pub fn derive_has_ui_state(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as ItemStruct);
|
||||
let name = input.ident;
|
||||
let Some(field) = input
|
||||
.fields
|
||||
.iter()
|
||||
.find(|f| f.ty == parse_quote!(UiState<Self>))
|
||||
else {
|
||||
return Error::new(
|
||||
name.span(),
|
||||
"could not find a UiState<Self> field for HasUiState",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into();
|
||||
};
|
||||
let field = &field.ident;
|
||||
quote! {
|
||||
impl HasUiState for #name {
|
||||
fn ui_state(&mut self) -> &mut UiState<Self> {
|
||||
&mut self.#field
|
||||
}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user