use std::fmt::Display; use proc_macro2::{TokenStream, TokenTree}; use quote::{ToTokens, TokenStreamExt}; use syn::parse::{Parse, ParseStream}; use syn::{braced, bracketed, token, AttrStyle, Attribute, Signature, Token, Visibility}; pub fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream { tokens.extend(error.into_compile_error()); tokens } pub fn error(s: &mut TokenStream, obj: A, msg: T) { s.extend(syn::Error::new_spanned(obj.into_token_stream(), msg).into_compile_error()) } /// Function signature and body. /// /// Same as `syn`'s `ItemFn` except we keep the body as a TokenStream instead of /// parsing it. This makes the macro not error if there's a syntax error in the body, /// which helps IDE autocomplete work better. #[derive(Debug, Clone)] pub struct ItemFn { pub attrs: Vec, pub vis: Visibility, pub sig: Signature, pub brace_token: token::Brace, pub body: TokenStream, } impl Parse for ItemFn { fn parse(input: ParseStream) -> syn::Result { let mut attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; let sig: Signature = input.parse()?; let content; let brace_token = braced!(content in input); while content.peek(Token![#]) && content.peek2(Token![!]) { let content2; attrs.push(Attribute { pound_token: content.parse()?, style: AttrStyle::Inner(content.parse()?), bracket_token: bracketed!(content2 in content), meta: content2.parse()?, }); } let mut body = Vec::new(); while !content.is_empty() { body.push(content.parse::()?); } let body = body.into_iter().collect(); Ok(ItemFn { attrs, vis, sig, brace_token, body, }) } } impl ToTokens for ItemFn { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.iter().filter(|a| matches!(a.style, AttrStyle::Outer))); self.vis.to_tokens(tokens); self.sig.to_tokens(tokens); self.brace_token.surround(tokens, |tokens| { tokens.append_all(self.body.clone()); }); } }