macro_input_core/
convert.rs

1use proc_macro2::Ident;
2use syn::{Error, Lit, Meta, NestedMeta, Result};
3
4/// a trait for extracting a value from `Option<syn::Meta>`
5pub trait FromMeta: Sized {
6    /// extract the value
7    ///
8    /// # Errors
9    /// may return an Error if the meta doesn't contain the correct value
10    fn from(meta: Option<Meta>) -> Result<Self>;
11}
12
13impl FromMeta for Option<()> {
14    fn from(meta: Option<Meta>) -> Result<Self> {
15        meta.map_or(Ok(None), |m| {
16            if matches!(m, Meta::Path(_)) {
17                Ok(Some(()))
18            } else {
19                Err(Error::new_spanned(m, "unexpected value"))
20            }
21        })
22    }
23}
24
25impl FromMeta for Vec<Ident> {
26    fn from(meta: Option<Meta>) -> Result<Self> {
27        meta.map_or(Ok(Vec::new()), |m| {
28            if let Meta::List(list) = m {
29                list.nested
30                    .iter()
31                    .map(|m| {
32                        if let NestedMeta::Meta(Meta::Path(m)) = m {
33                            m.get_ident()
34                                .cloned()
35                                .ok_or_else(|| Error::new_spanned(m, "expected ident"))
36                        } else {
37                            Err(Error::new_spanned(m, "expected ident"))
38                        }
39                    })
40                    .collect()
41            } else {
42                Err(Error::new_spanned(m, "expected idents"))
43            }
44        })
45    }
46}
47
48impl FromMeta for Option<Vec<Ident>> {
49    fn from(meta: Option<Meta>) -> Result<Self> {
50        meta.map_or(Ok(None), |m| {
51            if let Meta::List(list) = m {
52                list.nested
53                    .iter()
54                    .map(|m| {
55                        if let NestedMeta::Meta(Meta::Path(m)) = m {
56                            m.get_ident()
57                                .cloned()
58                                .ok_or_else(|| Error::new_spanned(m, "expected ident"))
59                        } else {
60                            Err(Error::new_spanned(m, "expected ident"))
61                        }
62                    })
63                    .collect::<Result<_>>()
64                    .map(Some)
65            } else {
66                Err(Error::new_spanned(m, "expected idents"))
67            }
68        })
69    }
70}
71
72/// a trait for extracting a value from a literal
73///
74/// [`FromMeta`] is automatically implemented for all implementations
75pub trait FromLit: Sized {
76    /// extract the value
77    ///
78    /// # Errors
79    /// may return an Error if the literal doesn't contain the correct value
80    fn from(lit: Option<Lit>) -> Result<Self>;
81}
82
83impl<F: FromLit> FromMeta for F {
84    fn from(meta: Option<Meta>) -> Result<Self> {
85        let lit = meta
86            .map(|m| match m {
87                Meta::NameValue(mnv) => Ok(mnv.lit),
88                _ => Err(Error::new_spanned(m, "expected named value")),
89            })
90            .transpose()?;
91        Self::from(lit)
92    }
93}
94
95impl FromLit for Option<Lit> {
96    fn from(lit: Option<Lit>) -> Result<Self> {
97        Ok(lit)
98    }
99}
100
101impl FromLit for String {
102    fn from(lit: Option<Lit>) -> Result<Self> {
103        if let Some(Lit::Str(v)) = lit {
104            Ok(v.value())
105        } else {
106            Err(Error::new_spanned(lit, "expected string"))
107        }
108    }
109}
110
111impl FromLit for Vec<u8> {
112    fn from(lit: Option<Lit>) -> Result<Self> {
113        if let Some(Lit::ByteStr(v)) = lit {
114            Ok(v.value())
115        } else {
116            Err(Error::new_spanned(lit, "expected bytes"))
117        }
118    }
119}
120
121impl FromLit for u8 {
122    fn from(lit: Option<Lit>) -> Result<Self> {
123        if let Some(Lit::Byte(v)) = lit {
124            Ok(v.value())
125        } else {
126            Err(Error::new_spanned(lit, "expected byte"))
127        }
128    }
129}
130
131impl FromLit for char {
132    fn from(lit: Option<Lit>) -> Result<Self> {
133        if let Some(Lit::Char(v)) = lit {
134            Ok(v.value())
135        } else {
136            Err(Error::new_spanned(lit, "expected char"))
137        }
138    }
139}
140
141impl FromLit for i32 {
142    fn from(lit: Option<Lit>) -> Result<Self> {
143        if let Some(Lit::Int(v)) = &lit {
144            v.base10_parse()
145        } else {
146            Err(Error::new_spanned(lit, "expected i32"))
147        }
148    }
149}
150
151impl FromLit for f32 {
152    fn from(lit: Option<Lit>) -> Result<Self> {
153        if let Some(Lit::Float(v)) = &lit {
154            v.base10_parse()
155        } else {
156            Err(Error::new_spanned(lit, "expected f32"))
157        }
158    }
159}
160
161impl FromLit for bool {
162    fn from(lit: Option<Lit>) -> Result<Self> {
163        if let Some(Lit::Bool(v)) = lit {
164            Ok(v.value)
165        } else {
166            Err(Error::new_spanned(lit, "expected bool"))
167        }
168    }
169}
170
171impl<V: FromLit> FromLit for Option<V> {
172    fn from(lit: Option<Lit>) -> Result<Self> {
173        if lit.is_some() {
174            Some(V::from(lit)).transpose()
175        } else {
176            Ok(None)
177        }
178    }
179}