1use syn::parse::{Parse, ParseStream};
2use syn::spanned::Spanned;
3use syn::punctuated::Punctuated;
4use syn::{Error, Path, Token, Lit, LitStr};
5
6use proc_macro2::Span;
7
8pub trait FromMeta: Sized {
10 fn from_meta(a: &MetaValue) -> Result<Self, Error>;
11}
12
13#[derive(Clone)]
15pub enum MetaValue {
16 Path(Path),
17 NameValue(MetaNameValue),
18 List(MetaList),
19 Lit(Lit),
20}
21
22impl MetaValue {
23 pub fn path(&self) -> Result<&Path, Error> {
26 match self {
27 Self::Path(p) => Ok(p),
28 Self::NameValue(nv) => Err(
29 Error::new(
30 nv.eq.span(),
31 "unexpected =; expected a naked path",
32 )
33 ),
34 Self::List(list) => Err(
35 Error::new(
36 list.paren.map(|p| p.span).unwrap_or_else(Span::call_site),
37 "unexpected (...); expected a naked path",
38 )
39 ),
40 Self::Lit(lit) => Err(
41 Error::new(
42 lit.span(),
43 "expected a naked path",
44 )
45 ),
46 }
47 }
48
49 pub fn name_value(&self) -> Result<&MetaNameValue, Error> {
52 match self {
53 Self::NameValue(nv) => Ok(nv),
54 Self::Path(p) => Err(
55 Error::new(
56 p.span(),
57 "expected =",
58 )
59 ),
60 Self::List(list) => Err(
61 Error::new(
62 list.paren.map(|p| p.span).unwrap_or_else(Span::call_site),
63 "expected =",
64 )
65 ),
66 Self::Lit(lit) => Err(
67 Error::new(
68 lit.span(),
69 "unexpected literal; expected =",
70 )
71 ),
72 }
73 }
74
75 pub fn lit(&self) -> Result<&Lit, Error> {
78 match self {
79 Self::Lit(lit) => Ok(lit),
80 Self::Path(p) => Err(
81 Error::new(
82 p.span(),
83 "expected a literal",
84 )
85 ),
86 Self::List(list) => Err(
87 Error::new(
88 list.name.span(),
89 "expected a literal",
90 )
91 ),
92 Self::NameValue(nv) => Err(
93 Error::new(
94 nv.name.span(),
95 "expected a literal",
96 )
97 ),
98 }
99 }
100
101 pub fn list(&self) -> Result<&MetaList, Error> {
104 match self {
105 Self::List(list) => Ok(list),
106 Self::Path(p) => Err(
107 Error::new(
108 p.span(),
109 "expected a list",
110 )
111 ),
112 Self::NameValue(nv) => Err(
113 Error::new(
114 nv.eq.span(),
115 "unexpected =; expected a list",
116 )
117 ),
118 Self::Lit(lit) => Err(
119 Error::new(
120 lit.span(),
121 "expected a list",
122 )
123 ),
124 }
125 }
126
127 pub fn value(&self) -> Result<&Lit, Error> {
134 match self {
135 Self::Lit(lit) => Ok(lit),
136 Self::NameValue(nv) => Ok(&nv.value),
137 Self::Path(p) => Err(
138 Error::new(
139 p.span(),
140 "expected a literal",
141 )
142 ),
143 Self::List(list) => Err(
144 Error::new(
145 list.paren.map(|p| p.span).unwrap_or_else(Span::call_site),
146 "expected a literal",
147 )
148 ),
149 }
150 }
151
152 pub fn name(&self) -> Option<&syn::Ident> {
153 let path = match self {
154 Self::Path(p) => p,
155 Self::List(list) => list.name.as_ref()?,
156 Self::NameValue(nv) => &nv.name,
157 _ => return None,
158 };
159
160 path.segments.last().map(|l| &l.ident)
161 }
162}
163
164impl From<Lit> for MetaValue {
165 fn from(l: Lit) -> MetaValue {
166 MetaValue::Lit(l)
167 }
168}
169
170impl From<MetaList> for MetaValue {
171 fn from(l: MetaList) -> MetaValue {
172 MetaValue::List(l)
173 }
174}
175
176impl Parse for MetaValue {
177 fn parse(p: ParseStream) -> Result<MetaValue, Error> {
178 if let Ok(name) = p.parse::<Path>() {
179 if p.peek(syn::token::Paren) {
181 let list;
183 Ok(MetaValue::List(
184 MetaList {
185 name: Some(name),
186 paren: Some(syn::parenthesized!(list in p)),
187 list: list.parse_terminated(Self::parse)?,
188 }
189 ))
190 } else if p.peek(Token![=]) {
191 Ok(MetaValue::NameValue(
193 MetaNameValue {
194 name,
195 eq: p.parse()?,
196 value: p.parse()?,
197 }
198 ))
199 } else {
200 Ok(MetaValue::Path(name))
202 }
203 } else {
204 p.parse::<Lit>().map(Into::into)
206 }
207 }
208}
209
210#[derive(Clone)]
212pub struct MetaNameValue {
213 pub name: Path,
214 pub eq: Token![=],
215 pub value: Lit,
216}
217
218#[derive(Clone, Default)]
222pub struct MetaList {
223 pub name: Option<Path>,
225 pub paren: Option<syn::token::Paren>,
227 pub list: Punctuated<MetaValue, Token![,]>,
228}
229
230impl MetaList {
231 pub fn get<T>(&self, name: &str) -> Option<Result<T, Error>>
236 where T:
237 FromMeta,
238 {
239 let item = self.list.iter()
240 .filter(|meta| meta.name().map(|n| n == name).unwrap_or(false))
241 .next()?;
242
243 Some(T::from_meta(&item))
245 }
246
247 pub fn parse_root_attr(p: ParseStream) -> Result<MetaList, Error> {
248 Ok(
249 MetaList {
250 name: None,
251 paren: None,
252 list: p.call(Punctuated::parse_terminated)?,
253 }
254 )
255 }
256}
257
258pub struct Meta<T>(pub T);
261
262impl<T> Meta<T> {
263 pub fn into_inner(self) -> T {
264 self.0
265 }
266}
267
268impl<T> std::ops::Deref for Meta<T> {
269 type Target = T;
270
271 fn deref(&self) -> &T {
272 &self.0
273 }
274}
275
276impl<T> Parse for Meta<T>
277where T:
278 FromMeta,
279{
280 fn parse(p: ParseStream) -> Result<Meta<T>, Error> {
281 if p.is_empty() {
282 T::from_meta(&MetaList::default().into())
284 .map(|t| Meta(t))
285 } else {
286 p.call(MetaList::parse_root_attr)
287 .map(Into::into)
288 .and_then(|meta| T::from_meta(&meta))
289 .map(|t| Meta(t))
290 }
291 }
292}
293
294impl FromMeta for LitStr {
296 fn from_meta(meta: &MetaValue) -> Result<LitStr, Error> {
297 let value = meta.value()?;
298
299 match &value {
300 Lit::Str(lit) => Ok(lit.clone()),
301 _ => Err(
302 Error::new(
303 value.span(),
304 "expected a string literal",
305 )
306 ),
307 }
308 }
309}
310
311impl<T> FromMeta for Option<T>
313where T:
314 FromMeta,
315{
316 fn from_meta(p: &MetaValue) -> Result<Option<T>, Error> {
317 Ok(Some(T::from_meta(p)?))
318 }
319}
320