use crate::{Error, Result}; use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree}; use quote::ToTokens; use std::iter::Peekable; pub(crate) fn parse_input( input: TokenStream, ) -> Result<(Vec, Vec, TokenTree)> { let mut input = input.into_iter().peekable(); let mut attrs = Vec::new(); while let Some(attr) = parse_next_attr(&mut input)? { attrs.push(attr); } let sig = parse_signature(&mut input); let body = input.next().ok_or_else(|| { Error::new( Span::call_site(), "`#[proc_macro_error]` can be applied only to functions".to_string(), ) })?; Ok((attrs, sig, body)) } fn parse_next_attr( input: &mut Peekable>, ) -> Result> { let shebang = match input.peek() { Some(TokenTree::Punct(ref punct)) if punct.as_char() == '#' => input.next().unwrap(), _ => return Ok(None), }; let group = match input.peek() { Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::Bracket => { let res = group.clone(); input.next(); res } other => { let span = other.map_or(Span::call_site(), |tt| tt.span()); return Err(Error::new(span, "expected `[`".to_string())); } }; let path = match group.stream().into_iter().next() { Some(TokenTree::Ident(ident)) => Some(ident), _ => None, }; Ok(Some(Attribute { shebang, group: TokenTree::Group(group), path, })) } fn parse_signature(input: &mut Peekable>) -> Vec { let mut sig = Vec::new(); loop { match input.peek() { Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::Brace => { return sig; } None => return sig, _ => sig.push(input.next().unwrap()), } } } pub(crate) struct Attribute { pub(crate) shebang: TokenTree, pub(crate) group: TokenTree, pub(crate) path: Option, } impl Attribute { pub(crate) fn path_is_ident(&self, ident: &str) -> bool { self.path.as_ref().map_or(false, |p| *p == ident) } } impl ToTokens for Attribute { fn to_tokens(&self, ts: &mut TokenStream) { self.shebang.to_tokens(ts); self.group.to_tokens(ts); } }