extern crate proc_macro; use proc_macro::TokenStream; use quote::quote; use syn::{ Attribute, Block, Error, GenericParam, Generics, Ident, ItemTrait, Signature, Token, Visibility, parse::{Parse, ParseStream, Result}, parse_macro_input, parse_quote, }; struct Input { attrs: Vec, vis: Visibility, name: Ident, generics: Generics, fns: Vec, } struct InputFn { sig: Signature, body: Block, } impl Parse for Input { fn parse(input: ParseStream) -> Result { let attrs = input.call(Attribute::parse_outer)?; let vis = input.parse()?; input.parse::()?; let name = input.parse()?; let generics = input.parse::()?; input.parse::()?; let mut fns = Vec::new(); while !input.is_empty() { let sig = input.parse()?; let body = input.parse()?; fns.push(InputFn { sig, body }) } if !input.is_empty() { input.error("function expected"); } Ok(Input { attrs, vis, name, generics, fns, }) } } #[proc_macro] pub fn widget_trait(input: TokenStream) -> TokenStream { let Input { attrs, vis, name, mut generics, fns, } = parse_macro_input!(input as Input); let sigs: Vec<_> = fns.iter().map(|f| f.sig.clone()).collect(); let impls: Vec<_> = fns .iter() .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 #generics { #(#sigs;)* } ); trai.attrs = attrs; quote! { #trai impl #generics #name for WL { #(#impls)* } } .into() }