use std::iter::Peekable;
use crate::proc_macro::{token_stream, TokenStream, TokenTree};
use crate::diag::SpanExt;
#[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) -> Result<Vec<ProxyType>, ()> {
let mut out = Vec::new();
let mut iter = args.into_iter().peekable();
while iter.peek().is_some() {
out.push(eat_type(&mut iter)?);
let comma_next = match iter.peek() {
Some(TokenTree::Punct(punct)) if punct.as_char() == ',' => true,
_ => false,
};
if comma_next {
let _ = iter.next();
}
}
Ok(out)
}
fn eat_type(iter: &mut Peekable<token_stream::IntoIter>) -> Result<ProxyType, ()> {
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) => {
group.span()
.err(format!("unexpected group. {}", EXPECTED_TEXT))
.note(NOTE_TEXT)
.emit();
return Err(());
}
TokenTree::Literal(lit) => {
lit.span()
.err(format!("unexpected literal. {}", EXPECTED_TEXT))
.note(NOTE_TEXT)
.emit();
return Err(());
}
TokenTree::Punct(punct) => {
if punct.as_char() != '&' {
let msg = format!("unexpected punctuation '{}'. {}", punct, EXPECTED_TEXT);
punct.span().err(msg).note(NOTE_TEXT).emit();
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,
_ => {
let msg = format!("unexpected '{}'. {}", ident, EXPECTED_TEXT);
ident.span()
.err(msg)
.note(NOTE_TEXT)
.emit();
return Err(());
}
}
}
};
Ok(ty)
}
#[cfg(test)]
mod test {
use crate::proc_macro::TokenStream;
use super::parse_types;
#[test]
fn empty() {
assert_eq!(
parse_types(TokenStream::new()),
Ok(vec![])
);
}
}