1use crate::{Error, Result}; 2use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree}; 3use quote::ToTokens; 4use std::iter::Peekable; 5 6pub(crate) fn parse_input( 7 input: TokenStream, 8) -> Result<(Vec<Attribute>, Vec<TokenTree>, TokenTree)> { 9 let mut input = input.into_iter().peekable(); 10 let mut attrs = Vec::new(); 11 12 while let Some(attr) = parse_next_attr(&mut input)? { 13 attrs.push(attr); 14 } 15 16 let sig = parse_signature(&mut input); 17 let body = input.next().ok_or_else(|| { 18 Error::new( 19 Span::call_site(), 20 "`#[proc_macro_error]` can be applied only to functions".to_string(), 21 ) 22 })?; 23 24 Ok((attrs, sig, body)) 25} 26 27fn parse_next_attr( 28 input: &mut Peekable<impl Iterator<Item = TokenTree>>, 29) -> Result<Option<Attribute>> { 30 let shebang = match input.peek() { 31 Some(TokenTree::Punct(ref punct)) if punct.as_char() == '#' => input.next().unwrap(), 32 _ => return Ok(None), 33 }; 34 35 let group = match input.peek() { 36 Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::Bracket => { 37 let res = group.clone(); 38 input.next(); 39 res 40 } 41 other => { 42 let span = other.map_or(Span::call_site(), |tt| tt.span()); 43 return Err(Error::new(span, "expected `[`".to_string())); 44 } 45 }; 46 47 let path = match group.stream().into_iter().next() { 48 Some(TokenTree::Ident(ident)) => Some(ident), 49 _ => None, 50 }; 51 52 Ok(Some(Attribute { 53 shebang, 54 group: TokenTree::Group(group), 55 path, 56 })) 57} 58 59fn parse_signature(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Vec<TokenTree> { 60 let mut sig = Vec::new(); 61 loop { 62 match input.peek() { 63 Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::Brace => { 64 return sig; 65 } 66 None => return sig, 67 _ => sig.push(input.next().unwrap()), 68 } 69 } 70} 71 72pub(crate) struct Attribute { 73 pub(crate) shebang: TokenTree, 74 pub(crate) group: TokenTree, 75 pub(crate) path: Option<Ident>, 76} 77 78impl Attribute { 79 pub(crate) fn path_is_ident(&self, ident: &str) -> bool { 80 self.path.as_ref().map_or(false, |p| *p == ident) 81 } 82} 83 84impl ToTokens for Attribute { 85 fn to_tokens(&self, ts: &mut TokenStream) { 86 self.shebang.to_tokens(ts); 87 self.group.to_tokens(ts); 88 } 89} 90