1use std::convert::TryFrom;
2
3use quote::{ToTokens, TokenStreamExt};
4use proc_macro2::{Span, TokenStream, TokenTree};
5use proc_macro2_diagnostics::{Diagnostic, SpanDiagnosticExt};
6use syn::{self, punctuated::Punctuated, spanned::Spanned, parse::{Parse, Parser}};
7
8use generator::Result;
9
10#[derive(Debug, Clone)]
11pub enum MetaItem {
12 Path(syn::Path),
13 Tokens(TokenStream),
14 KeyValue {
15 path: syn::Path,
16 eq: syn::Token![=],
17 tokens: TokenStream,
18 },
19 List {
20 path: syn::Path,
21 delimiter: syn::MacroDelimiter,
22 items: Punctuated<MetaItem, syn::token::Comma>
23 }
24}
25
26fn parse_delimited_tokens(input: syn::parse::ParseStream) -> syn::Result<TokenStream> {
27 input.step(|cursor| {
28 let mut stream = TokenStream::new();
29 let mut rest = *cursor;
30 while let Some((tt, next)) = rest.token_tree() {
31 if matches!(&tt, TokenTree::Punct(p) if p.as_char() == ',') {
32 return Ok((stream, rest));
33 }
34
35 rest = next;
36 stream.append(tt);
37 }
38
39 Ok((stream, rest))
40 })
41}
42
43macro_rules! macro_delimited {
44 ($a:ident in $input:ident) => {
45 if $input.peek(syn::token::Brace) {
46 syn::MacroDelimiter::Brace(syn::braced!($a in $input))
47 } else if $input.peek(syn::token::Bracket) {
48 syn::MacroDelimiter::Bracket(syn::bracketed!($a in $input))
49 } else {
50 syn::MacroDelimiter::Paren(syn::parenthesized!($a in $input))
51 }
52 };
53}
54
55impl syn::parse::Parse for MetaItem {
56 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
57 let item = if let Ok(path) = input.parse::<syn::Path>() {
58 if input.peek(syn::token::Paren) {
59 let list;
60 MetaItem::List {
61 path,
62 delimiter: macro_delimited!(list in input),
63 items: list.parse_terminated(Self::parse, syn::Token![,])?
64 }
65 } else if input.peek(syn::Token![=]) {
66 MetaItem::KeyValue {
67 path,
68 eq: input.parse()?,
69 tokens: parse_delimited_tokens(input)?,
70 }
71 } else {
72 MetaItem::Path(path)
73 }
74 } else {
75 MetaItem::Tokens(parse_delimited_tokens(input)?)
76 };
77
78 Ok(item)
79 }
80}
81
82impl TryFrom<syn::Meta> for MetaItem {
83 type Error = syn::Error;
84
85 fn try_from(value: syn::Meta) -> std::result::Result<Self, Self::Error> {
86 let item = match value {
87 syn::Meta::Path(path) => MetaItem::Path(path),
88 syn::Meta::List(list) => MetaItem::List {
89 path: list.path,
90 delimiter: list.delimiter,
91 items: <Punctuated<Self, syn::Token![,]>>::parse_terminated.parse2(list.tokens)?,
92 },
93 syn::Meta::NameValue(nv) => MetaItem::KeyValue {
94 path: nv.path,
95 eq: nv.eq_token,
96 tokens: parse_delimited_tokens.parse2(nv.value.to_token_stream())?,
97 }
98 };
99
100 Ok(item)
101 }
102}
103
104impl MetaItem {
105 pub fn attr_path(&self) -> Option<&syn::Path> {
106 use MetaItem::*;
107
108 match self {
109 Path(p) => Some(p),
110 KeyValue { path, .. } => Some(path),
111 List { path, .. } => Some(path),
112 _ => None
113 }
114 }
115
116 pub fn name(&self) -> Option<&syn::Ident> {
117 let path = self.attr_path()?;
118 path.segments.last().map(|l| &l.ident)
119 }
120
121 pub fn tokens(&self) -> Option<&TokenStream> {
122 match self {
123 MetaItem::Tokens(tokens) | MetaItem::KeyValue { tokens, .. } => Some(tokens),
124 _ => None
125 }
126 }
127
128 pub fn parse_value<T: Parse>(&self, expected: &str) -> Result<T> {
129 let tokens = self.tokens().ok_or_else(|| self.expected(expected))?;
130 syn::parse2(tokens.clone())
131 .map_err(|e| e.span().error(format!("failed to parse {}: {}", expected, e)))
132 }
133
134 pub fn parse_value_with<P: Parser>(&self, parser: P, expected: &str) -> Result<P::Output> {
135 match self {
136 MetaItem::Tokens(tokens) | MetaItem::KeyValue { tokens, .. } => {
137 parser.parse2(tokens.clone()).map_err(|e| {
138 e.span().error(format!("failed to parse {}: {}", expected, e))
139 })
140 },
141 _ => Err(self.expected(expected))
142 }
143 }
144
145 pub fn expected(&self, k: &str) -> Diagnostic {
146 let bare = self.is_bare().then_some("bare ").unwrap_or("");
147 let msg = match self.name().map(|i| i.to_string()) {
148 Some(n) => format!("expected {}, found {}{} {:?}", k, bare, self.description(), n),
149 None => format!("expected {}, found {}{}", k, bare, self.description()),
150 };
151
152 self.span().error(msg)
153 }
154
155 pub fn description(&self) -> &'static str {
156 let expr = self.tokens().and_then(|t| syn::parse2::<syn::Expr>(t.clone()).ok());
157 if let Some(syn::Expr::Lit(e)) = expr {
158 match e.lit {
159 syn::Lit::Str(..) => "string literal",
160 syn::Lit::ByteStr(..) => "byte string literal",
161 syn::Lit::Byte(..) => "byte literal",
162 syn::Lit::Char(..) => "character literal",
163 syn::Lit::Int(..) => "integer literal",
164 syn::Lit::Float(..) => "float literal",
165 syn::Lit::Bool(..) => "boolean literal",
166 syn::Lit::Verbatim(..) => "literal",
167 _ => "unknown literal"
168 }
169 } else if expr.is_some() {
170 "non-literal expression"
171 } else {
172 match self {
173 MetaItem::Tokens(..) => "tokens",
174 MetaItem::KeyValue { .. } => "key/value pair",
175 MetaItem::List { .. } => "list",
176 MetaItem::Path(_) => "path",
177 }
178 }
179 }
180
181 pub fn is_bare(&self) -> bool {
182 match self {
183 MetaItem::Path(..) | MetaItem::Tokens(..) => true,
184 MetaItem::KeyValue { .. } | MetaItem::List { .. } => false,
185 }
186 }
187
188 pub fn expr(&self) -> Result<syn::Expr> {
189 self.parse_value("expression")
190 }
191
192 pub fn path(&self) -> Result<syn::Path> {
193 match self {
194 MetaItem::Path(p) => Ok(p.clone()),
195 _ => self.parse_value("path")
196 }
197 }
198
199 pub fn lit(&self) -> Result<syn::Lit> {
200 fn from_expr(meta: &MetaItem, expr: syn::Expr) -> Result<syn::Lit> {
201 match expr {
202 syn::Expr::Lit(e) => Ok(e.lit),
203 syn::Expr::Group(g) => from_expr(meta, *g.expr),
204 _ => Err(meta.expected("literal")),
205 }
206 }
207
208 self.parse_value("literal").and_then(|e| from_expr(self, e))
209 }
210
211 pub fn list(&self) -> Result<impl Iterator<Item = &MetaItem> + Clone> {
212 match self {
213 MetaItem::List { items, .. } => Ok(items.iter()),
214 _ => {
215 let n = self.name().map(|i| i.to_string()).unwrap_or_else(|| "attr".into());
216 Err(self.expected(&format!("list `#[{}(..)]`", n)))
217 }
218 }
219 }
220
221 pub fn value_span(&self) -> Span {
222 match self {
223 MetaItem::KeyValue { tokens, .. } => tokens.span(),
224 _ => self.span(),
225 }
226 }
227}
228
229impl ToTokens for MetaItem {
230 fn to_tokens(&self, stream: &mut TokenStream) {
231 match self {
232 MetaItem::Path(p) => p.to_tokens(stream),
233 MetaItem::Tokens(tokens) => stream.append_all(tokens.clone()),
234 MetaItem::KeyValue { path, eq, tokens } => {
235 path.to_tokens(stream);
236 eq.to_tokens(stream);
237 stream.append_all(tokens.clone());
238 }
239 MetaItem::List { path, delimiter, items } => {
240 use syn::MacroDelimiter::*;
241
242 path.to_tokens(stream);
243 match delimiter {
244 Paren(p) => p.surround(stream, |t| items.to_tokens(t)),
245 Brace(b) => b.surround(stream, |t| items.to_tokens(t)),
246 Bracket(b) => b.surround(stream, |t| items.to_tokens(t)),
247 }
248 }
249 }
250 }
251}