use std::iter::Peekable;
use syn::Error;
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 {
matches!(*self, ProxyType::Fn | ProxyType::FnMut | ProxyType::FnOnce)
}
}
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 =
matches!(iter.peek(), Some(TokenTree::Punct(punct)) if punct.as_char() == ',');
if comma_next {
let _ = iter.next();
}
}
out
}
fn eat_type(iter: &mut Peekable<token_stream::IntoIter>) -> syn::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) => {
return Err(Error::new(
group.span().into(),
format_args!("unexpected group, {}\n{}", EXPECTED_TEXT, NOTE_TEXT),
));
}
TokenTree::Literal(lit) => {
return Err(Error::new(
lit.span().into(),
format_args!("unexpected literal, {}\n{}", EXPECTED_TEXT, NOTE_TEXT),
));
}
TokenTree::Punct(punct) => {
if punct.as_char() != '&' {
return Err(Error::new(
punct.span().into(),
format_args!(
"unexpected punctuation '{}', {}\n{}",
punct, EXPECTED_TEXT, NOTE_TEXT
),
));
}
let is_mut_next =
matches!(iter.peek(), Some(TokenTree::Ident(id)) if id.to_string() == "mut");
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,
_ => {
return Err(Error::new(
ident.span().into(),
format_args!("unexpected '{}', {}\n{}", ident, EXPECTED_TEXT, NOTE_TEXT),
));
}
},
};
Ok(ty)
}