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<Attribute>, Vec<TokenTree>, 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<impl Iterator<Item = TokenTree>>,
) -> Result<Option<Attribute>> {
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<impl Iterator<Item = TokenTree>>) -> Vec<TokenTree> {
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<Ident>,
}
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);
}
}