use proc_macro_error::emit_error;
use std::iter::Peekable;
use crate::proc_macro::{token_stream, TokenStream, TokenTree};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ProxyType {
Ref,
RefMut,
Arc,
Rc,
Box,
Fn,
FnMut,
FnOnce,
}
impl ProxyType {
pub(crate) fn is_fn(&self) -> bool {
match *self {
ProxyType::Fn | ProxyType::FnMut | ProxyType::FnOnce => true,
_ => false,
}
}
}
pub(crate) fn parse_types(args: TokenStream) -> Vec<ProxyType> {
let mut out = Vec::new();
let mut iter = args.into_iter().peekable();
while iter.peek().is_some() {
if let Ok(ty) = eat_type(&mut iter) {
out.push(ty);
}
let comma_next = match iter.peek() {
Some(TokenTree::Punct(punct)) if punct.as_char() == ',' => true,
_ => false,
};
if comma_next {
let _ = iter.next();
}
}
out
}
fn eat_type(iter: &mut Peekable<token_stream::IntoIter>) -> Result<ProxyType, ()> {
#[rustfmt::skip]
const NOTE_TEXT: &str = "\
attribute format should be `#[auto_impl(<types>)]` where `<types>` is \
a comma-separated list of types. Allowed values for types: `&`, \
`&mut`, `Box`, `Rc`, `Arc`, `Fn`, `FnMut` and `FnOnce`.\
";
const EXPECTED_TEXT: &str = "expected '&' or ident.";
let ty = match iter.next().unwrap() {
TokenTree::Group(group) => {
emit_error!(
group.span(),
"unexpected group, {}", EXPECTED_TEXT;
note = NOTE_TEXT;
);
return Err(());
}
TokenTree::Literal(lit) => {
emit_error!(
lit.span(),
"unexpected literal, {}", EXPECTED_TEXT;
note = NOTE_TEXT;
);
return Err(());
}
TokenTree::Punct(punct) => {
if punct.as_char() != '&' {
emit_error!(
punct.span(),
"unexpected punctuation '{}', {}", punct, EXPECTED_TEXT;
note = NOTE_TEXT;
);
return Err(());
}
let is_mut_next = match iter.peek() {
Some(TokenTree::Ident(id)) if id.to_string() == "mut" => true,
_ => false,
};
if is_mut_next {
let _ = iter.next();
ProxyType::RefMut
} else {
ProxyType::Ref
}
}
TokenTree::Ident(ident) => match &*ident.to_string() {
"Box" => ProxyType::Box,
"Rc" => ProxyType::Rc,
"Arc" => ProxyType::Arc,
"Fn" => ProxyType::Fn,
"FnMut" => ProxyType::FnMut,
"FnOnce" => ProxyType::FnOnce,
_ => {
emit_error!(
ident.span(),
"unexpected '{}', {}", ident, EXPECTED_TEXT;
note = NOTE_TEXT;
);
return Err(());
}
},
};
Ok(ty)
}