evitable_syn_meta_ext/
from_meta.rs1use crate::quote::ToTokens;
2use proc_macro2::TokenStream;
3use std::rc::Rc;
4use std::sync::atomic::AtomicBool;
5use std::sync::Arc;
6use syn::spanned::Spanned;
7use syn::{Ident, Lit, Path};
8
9use super::error::{Error, Result};
10use super::{Meta, MetaValue, NestedMeta};
11
12pub trait FromMeta: Sized {
13 fn from_nested_meta(item: &NestedMeta) -> Result<Self> {
14 (match item {
15 NestedMeta::Literal(lit) => Self::from_lit(lit),
16 NestedMeta::Meta(mi) => match mi {
17 Meta::Path(p) => Self::from_path(p),
18 Meta::NameValue(_) => Err(Error::unexpected_type("name value").with_span(item)),
19 Meta::List(_) => Err(Error::unexpected_type("list").with_span(item)),
20 },
21 })
22 .map_err(|e| e.with_span(item))
23 }
24
25 fn from_meta(item: &Meta) -> Result<Self> {
34 (match item {
35 Meta::Path(_) => Self::from_empty(),
36 Meta::List(value) => {
37 Self::from_list(value.nested.iter().collect::<Vec<&NestedMeta>>().as_ref())
38 }
39 Meta::NameValue(value) => Self::from_value(&value.val),
40 })
41 .map_err(|e| e.with_span(item))
42 }
43
44 fn from_empty() -> Result<Self> {
47 Err(Error::unsupported_format("empty"))
48 }
49
50 #[allow(unused_variables)]
52 fn from_list(items: &[&NestedMeta]) -> Result<Self> {
53 match items.len() {
54 0 => Self::from_empty(),
55 1 => Self::from_nested_meta(&items[0]),
56 _ => Err(Error::unsupported_format("list")),
57 }
58 }
59
60 fn from_value(value: &MetaValue) -> Result<Self> {
68 (match value {
69 MetaValue::Path(path) => Self::from_path(path),
70 MetaValue::Literal(lit) => Self::from_lit(lit),
71 })
72 .map_err(|e| e.with_span(value))
73 }
74
75 fn from_lit(lit: &Lit) -> Result<Self> {
76 (match lit {
77 Lit::Bool(b) => Self::from_bool(b.value, b),
78 Lit::Str(s) => Self::from_string(&s.value(), s),
79 Lit::Char(c) => Self::from_char(c.value(), c),
80 Lit::Int(i) => Self::from_int(i.base10_parse()?, i),
81 _ => Err(Error::unexpected_lit_type(lit)),
82 })
83 .map_err(|e| e.with_span(lit))
84 }
85
86 #[allow(unused_variables)]
88 fn from_char<S: Spanned>(value: char, span: &S) -> Result<Self> {
89 Err(Error::unexpected_type("char").with_span(span))
90 }
91
92 #[allow(unused_variables)]
94 fn from_int<S: Spanned>(value: u64, span: &S) -> Result<Self> {
95 Err(Error::unexpected_type("int").with_span(span))
96 }
97
98 #[allow(unused_variables)]
100 fn from_path(value: &Path) -> Result<Self> {
101 if value.leading_colon.is_none()
102 && value.segments.len() == 1
103 && value.segments[0].arguments == syn::PathArguments::None
104 {
105 Self::from_ident(&value.segments[0].ident)
106 } else {
107 Err(Error::unexpected_type("path"))
108 }
109 }
110
111 #[allow(unused_variables)]
113 fn from_string<S: Spanned>(value: &str, span: &S) -> Result<Self> {
114 Err(Error::unexpected_type("string").with_span(span))
115 }
116
117 #[allow(unused_variables)]
119 fn from_bool<S: Spanned>(value: bool, span: &S) -> Result<Self> {
120 Err(Error::unexpected_type("bool").with_span(span))
121 }
122
123 #[allow(unused_variables)]
124 fn from_ident(value: &Ident) -> Result<Self> {
125 Err(Error::unexpected_type("ident"))
126 }
127}
128
129impl FromMeta for () {
132 fn from_empty() -> Result<Self> {
133 Ok(())
134 }
135}
136
137impl FromMeta for bool {
138 fn from_empty() -> Result<Self> {
139 Ok(true)
140 }
141
142 fn from_bool<S: Spanned>(value: bool, _: &S) -> Result<Self> {
143 Ok(value)
144 }
145
146 fn from_string<S: Spanned>(value: &str, span: &S) -> Result<Self> {
147 value
148 .parse()
149 .map_err(|_| Error::unknown_value(value).with_span(span))
150 }
151}
152
153impl FromMeta for AtomicBool {
154 fn from_meta(mi: &Meta) -> Result<Self> {
155 FromMeta::from_meta(mi)
156 .map(AtomicBool::new)
157 .map_err(|e| e.with_span(mi))
158 }
159}
160
161impl FromMeta for String {
162 fn from_string<S: Spanned>(s: &str, _span: &S) -> Result<Self> {
163 Ok(s.to_string())
164 }
165
166 fn from_path(p: &Path) -> Result<Self> {
167 let mut ss = TokenStream::new();
168 p.to_tokens(&mut ss);
169 Ok(format!("{}", ss))
170 }
171}
172
173macro_rules! from_meta_num {
176 ($ty:ident) => {
177 impl FromMeta for $ty {
178 fn from_string<S: Spanned>(s: &str, span: &S) -> Result<Self> {
179 s.parse()
180 .map_err(|_| Error::unknown_value(s).with_span(span))
181 }
182
183 fn from_lit(value: &Lit) -> Result<Self> {
184 (match value {
185 Lit::Str(s) => Self::from_string(&s.value(), s),
186 Lit::Int(s) => Ok(s.base10_parse()?),
187 v => Err(Error::unexpected_lit_type(value).with_span(&v)),
188 })
189 .map_err(|e| e.with_span(value))
190 }
191 }
192 };
193}
194
195from_meta_num!(u8);
196from_meta_num!(u16);
197from_meta_num!(u32);
198from_meta_num!(u64);
199from_meta_num!(usize);
200from_meta_num!(i8);
201from_meta_num!(i16);
202from_meta_num!(i32);
203from_meta_num!(i64);
204from_meta_num!(isize);
205
206macro_rules! from_meta_float {
209 ($ty:ident) => {
210 impl FromMeta for $ty {
211 fn from_string<S: Spanned>(s: &str, span: &S) -> Result<Self> {
212 s.parse()
213 .map_err(|_| Error::unknown_value(s).with_span(span))
214 }
215
216 fn from_lit(value: &Lit) -> Result<Self> {
217 (match value {
218 Lit::Str(s) => Self::from_string(&s.value(), s),
219 Lit::Float(s) => Ok(s.base10_parse()?),
220 v => Err(Error::unexpected_lit_type(value).with_span(&v)),
221 })
222 .map_err(|e| e.with_span(value))
223 }
224 }
225 };
226}
227
228from_meta_float!(f32);
229from_meta_float!(f64);
230
231impl FromMeta for syn::Ident {
235 fn from_ident(value: &Ident) -> Result<Self> {
236 Ok(value.clone())
237 }
238
239 fn from_string<S: Spanned>(value: &str, span: &S) -> Result<Self> {
240 Ok(syn::Ident::new(value, span.span()))
241 }
242}
243
244impl FromMeta for syn::Path {
247 fn from_path(path: &Path) -> Result<Self> {
248 Ok(path.clone())
249 }
250
251 fn from_string<S: Spanned>(value: &str, span: &S) -> Result<Self> {
252 syn::parse_str(value).map_err(|_| Error::unknown_value(value).with_span(span))
253 }
254
255 fn from_lit(value: &Lit) -> Result<Self> {
256 if let Lit::Str(ref path_str) = *value {
257 path_str
258 .parse()
259 .map_err(|_| Error::unknown_lit_str_value(path_str).with_span(value))
260 } else {
261 Err(Error::unexpected_lit_type(value).with_span(value))
262 }
263 }
264}
265
266impl FromMeta for syn::Lit {
267 fn from_lit(value: &Lit) -> Result<Self> {
268 Ok(value.clone())
269 }
270}
271
272macro_rules! from_meta_lit {
273 ($impl_ty:path, $lit_variant:path) => {
274 impl FromMeta for $impl_ty {
275 fn from_lit(value: &Lit) -> Result<Self> {
276 if let $lit_variant(ref value) = *value {
277 Ok(value.clone())
278 } else {
279 Err(Error::unexpected_lit_type(value).with_span(value))
280 }
281 }
282 }
283 };
284}
285
286from_meta_lit!(syn::LitInt, Lit::Int);
287from_meta_lit!(syn::LitFloat, Lit::Float);
288from_meta_lit!(syn::LitStr, Lit::Str);
289from_meta_lit!(syn::LitByte, Lit::Byte);
290from_meta_lit!(syn::LitByteStr, Lit::ByteStr);
291from_meta_lit!(syn::LitChar, Lit::Char);
292from_meta_lit!(syn::LitBool, Lit::Bool);
293from_meta_lit!(proc_macro2::Literal, Lit::Verbatim);
294
295impl FromMeta for Meta {
296 fn from_meta(value: &Meta) -> Result<Self> {
297 Ok(value.clone())
298 }
299}
300
301impl FromMeta for ident_case::RenameRule {
302 fn from_string<S: Spanned>(value: &str, span: &S) -> Result<Self> {
303 value
304 .parse()
305 .map_err(|_| Error::unknown_value(value).with_span(span))
306 }
307}
308
309impl<T: FromMeta> FromMeta for Option<T> {
310 fn from_meta(item: &Meta) -> Result<Self> {
311 FromMeta::from_meta(item).map(Some)
312 }
313}
314
315impl<T: FromMeta> FromMeta for Box<T> {
316 fn from_meta(item: &Meta) -> Result<Self> {
317 FromMeta::from_meta(item).map(Box::new)
318 }
319}
320
321impl<T: FromMeta> FromMeta for Result<T> {
322 fn from_meta(item: &Meta) -> Result<Self> {
323 Ok(FromMeta::from_meta(item))
324 }
325}
326
327impl<T: FromMeta> FromMeta for ::std::result::Result<T, Meta> {
330 fn from_meta(item: &Meta) -> Result<Self> {
331 T::from_meta(item)
332 .map(Ok)
333 .or_else(|_| Ok(Err(item.clone())))
334 }
335}
336
337impl<T: FromMeta> FromMeta for Rc<T> {
338 fn from_meta(item: &Meta) -> Result<Self> {
339 FromMeta::from_meta(item).map(Rc::new)
340 }
341}
342
343impl<T: FromMeta> FromMeta for Arc<T> {
344 fn from_meta(item: &Meta) -> Result<Self> {
345 FromMeta::from_meta(item).map(Arc::new)
346 }
347}
348
349impl<T: FromMeta> FromMeta for Vec<T> {
350 fn from_empty() -> Result<Self> {
351 Ok(Vec::new())
352 }
353
354 fn from_list(items: &[&NestedMeta]) -> Result<Self> {
355 let mut ret = Vec::with_capacity(items.len());
356 for item in items {
357 ret.push(T::from_nested_meta(item)?);
358 }
359
360 Ok(ret)
361 }
362
363 fn from_value(val: &MetaValue) -> Result<Self> {
364 let mut ret = Vec::with_capacity(1);
365 ret.push(T::from_value(val)?);
366 Ok(ret)
367 }
368}
369
370#[cfg(test)]
373mod tests {
374 use proc_macro2::TokenStream;
375 use syn;
376
377 use super::Meta;
378 use super::{FromMeta, Result};
379 use crate::AttrExt;
380
381 fn pm(tokens: TokenStream) -> ::std::result::Result<Meta, String> {
383 let attribute: syn::Attribute = parse_quote!(#[#tokens]);
384 attribute.meta().map_err(|_| "Unable to parse".into())
385 }
386
387 fn fm<T: FromMeta>(tokens: TokenStream) -> T {
388 FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
389 .expect("Tests should pass valid input")
390 }
391
392 #[test]
393 fn unit_succeeds() {
394 assert_eq!(fm::<()>(quote!(ignore)), ());
395 }
396
397 #[test]
398 fn bool_succeeds() {
399 assert_eq!(fm::<bool>(quote!(ignore)), true);
401
402 assert_eq!(fm::<bool>(quote!(ignore = true)), true);
404 assert_eq!(fm::<bool>(quote!(ignore = false)), false);
405
406 assert_eq!(fm::<bool>(quote!(ignore = "true")), true);
408 assert_eq!(fm::<bool>(quote!(ignore = "false")), false);
409 }
410
411 #[test]
412 fn string_succeeds() {
413 assert_eq!(&fm::<String>(quote!(ignore = "world")), "world");
415
416 assert_eq!(&fm::<String>(quote!(ignore = r#"world"#)), "world");
418 }
419
420 #[test]
421 fn number_succeeds() {
422 assert_eq!(fm::<u8>(quote!(ignore = "2")), 2u8);
423 assert_eq!(fm::<i16>(quote!(ignore = "-25")), -25i16);
424 assert_eq!(fm::<f64>(quote!(ignore = "1.4e10")), 1.4e10);
425 }
426
427 #[test]
428 fn int_without_quotes() {
429 assert_eq!(fm::<u8>(quote!(ignore = 2)), 2u8);
430 assert_eq!(fm::<u16>(quote!(ignore = 255)), 255u16);
431 assert_eq!(fm::<u32>(quote!(ignore = 5000)), 5000u32);
432
433 assert_eq!(fm::<u32>(quote!(ignore = 5000i32)), 5000u32);
435 }
436
437 #[test]
438 fn float_without_quotes() {
439 assert_eq!(fm::<f32>(quote!(ignore = 2.)), 2.0f32);
440 assert_eq!(fm::<f32>(quote!(ignore = 2.0)), 2.0f32);
441 assert_eq!(fm::<f64>(quote!(ignore = 1.4e10)), 1.4e10f64);
442 }
443
444 #[test]
445 fn meta_succeeds() {
446 assert_eq!(
447 fm::<Meta>(quote!(hello(world, today))),
448 pm(quote!(hello(world, today))).unwrap()
449 );
450 }
451
452 #[test]
455 fn result_succeeds() {
456 fm::<Result<()>>(quote!(ignore)).unwrap();
457 fm::<Result<()>>(quote!(ignore(world))).unwrap_err();
458 }
459}