1use std::iter::Peekable;
2use syn::Error;
3
4use crate::proc_macro::{token_stream, TokenStream, TokenTree};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub(crate) enum ProxyType {
9 Ref,
10 RefMut,
11 Arc,
12 Rc,
13 Box,
14 Fn,
15 FnMut,
16 FnOnce,
17}
18
19impl ProxyType {
20 pub(crate) fn is_fn(&self) -> bool {
21 matches!(*self, ProxyType::Fn | ProxyType::FnMut | ProxyType::FnOnce)
22 }
23}
24
25pub(crate) fn parse_types(args: TokenStream) -> Vec<ProxyType> {
35 let mut out = Vec::new();
36 let mut iter = args.into_iter().peekable();
37
38 while iter.peek().is_some() {
40 if let Ok(ty) = eat_type(&mut iter) {
42 out.push(ty);
43 }
44
45 let comma_next =
49 matches!(iter.peek(), Some(TokenTree::Punct(punct)) if punct.as_char() == ',');
50
51 if comma_next {
52 let _ = iter.next();
53 }
54 }
55
56 out
57}
58
59fn eat_type(iter: &mut Peekable<token_stream::IntoIter>) -> syn::Result<ProxyType> {
62 #[rustfmt::skip]
63 const NOTE_TEXT: &str = "\
64 attribute format should be `#[auto_impl(<types>)]` where `<types>` is \
65 a comma-separated list of types. Allowed values for types: `&`, \
66 `&mut`, `Box`, `Rc`, `Arc`, `Fn`, `FnMut` and `FnOnce`.\
67 ";
68 const EXPECTED_TEXT: &str = "expected '&' or ident.";
69
70 let ty = match iter.next().unwrap() {
73 TokenTree::Group(group) => {
74 return Err(Error::new(
75 group.span().into(),
76 format_args!("unexpected group, {}\n{}", EXPECTED_TEXT, NOTE_TEXT),
77 ));
78 }
79
80 TokenTree::Literal(lit) => {
81 return Err(Error::new(
82 lit.span().into(),
83 format_args!("unexpected literal, {}\n{}", EXPECTED_TEXT, NOTE_TEXT),
84 ));
85 }
86
87 TokenTree::Punct(punct) => {
88 if punct.as_char() != '&' {
90 return Err(Error::new(
91 punct.span().into(),
92 format_args!(
93 "unexpected punctuation '{}', {}\n{}",
94 punct, EXPECTED_TEXT, NOTE_TEXT
95 ),
96 ));
97 }
98
99 let is_mut_next =
101 matches!(iter.peek(), Some(TokenTree::Ident(id)) if id.to_string() == "mut");
102
103 if is_mut_next {
104 let _ = iter.next();
106 ProxyType::RefMut
107 } else {
108 ProxyType::Ref
109 }
110 }
111
112 TokenTree::Ident(ident) => match &*ident.to_string() {
113 "Box" => ProxyType::Box,
114 "Rc" => ProxyType::Rc,
115 "Arc" => ProxyType::Arc,
116 "Fn" => ProxyType::Fn,
117 "FnMut" => ProxyType::FnMut,
118 "FnOnce" => ProxyType::FnOnce,
119 _ => {
120 return Err(Error::new(
121 ident.span().into(),
122 format_args!("unexpected '{}', {}\n{}", ident, EXPECTED_TEXT, NOTE_TEXT),
123 ));
124 }
125 },
126 };
127
128 Ok(ty)
129}
130
131