macro_input_core/
ty.rs

1#[cfg(feature = "legacy")]
2use macro_compose::{Collector, Lint};
3#[cfg(feature = "legacy")]
4use quote::ToTokens;
5use std::convert::TryFrom;
6#[cfg(feature = "legacy")]
7use syn::Lit;
8use syn::{parse_quote, Error, GenericArgument, Path, PathArguments};
9
10#[derive(Clone, Copy, Debug)]
11/// the type of a field
12pub struct Type {
13    /// the actual type
14    pub ty: Types,
15    /// whether or not the value for the type is optional
16    pub optional: bool,
17}
18
19#[cfg(feature = "legacy")]
20impl<'a> Lint<Option<&'a Lit>> for Type {
21    fn lint(&self, input: &Option<&'a Lit>, c: &mut Collector) {
22        let ty = match self.ty {
23            Types::Any => "anything",
24            Types::Flag => "nothing",
25            Types::Str => "string",
26            Types::ByteStr => "byte string",
27            Types::Byte => "byte",
28            Types::Char => "char",
29            Types::I32 => "i32",
30            Types::F32 => "f32",
31            Types::Bool => "bool",
32            Types::Idents => "idents",
33        };
34
35        match (input, self.ty) {
36            (Some(_), Types::Any)
37            | (None, Types::Flag)
38            | (Some(Lit::Str(_)), Types::Str)
39            | (Some(Lit::ByteStr(_)), Types::ByteStr)
40            | (Some(Lit::Byte(_)), Types::Byte)
41            | (Some(Lit::Char(_)), Types::Char)
42            | (Some(Lit::Int(_)), Types::I32)
43            | (Some(Lit::Float(_)), Types::F32)
44            | (Some(Lit::Bool(_)), Types::Bool) => {}
45            (None, _) if self.optional => {}
46            (Some(lit), _) => c.error(Error::new_spanned(
47                input,
48                format!("expected {}, got {}", ty, lit.to_token_stream()),
49            )),
50            (None, _) => c.error(Error::new_spanned(
51                input,
52                format!("expected {}, got nothing", ty,),
53            )),
54        }
55    }
56}
57#[derive(Clone, Copy, Debug)]
58/// all the types a field can contain
59pub enum Types {
60    /// can contain any literal
61    Any,
62    /// doesn't have a value eg `#[my_input(enabled)]`
63    Flag,
64    /// for string
65    Str,
66    /// for bytestring
67    ByteStr,
68    /// for u8
69    Byte,
70    /// for char
71    Char,
72    /// for i32
73    I32,
74    /// for f32
75    F32,
76    /// for bool
77    Bool,
78    /// for idents
79    Idents,
80}
81
82impl TryFrom<&syn::Type> for Type {
83    type Error = Error;
84
85    fn try_from(ty: &syn::Type) -> Result<Self, Self::Error> {
86        let byte_vec_path: Path = parse_quote!(Vec<u8>);
87        let ident_vec_path: Path = parse_quote!(Vec<Ident>);
88
89        let error = || Err(Error::new_spanned(&ty, "unexpected type"));
90
91        match ty {
92            syn::Type::Path(p) => {
93                if p.path.is_ident("String") {
94                    Ok(Type {
95                        ty: Types::Str,
96                        optional: false,
97                    })
98                } else if p.path == byte_vec_path {
99                    Ok(Type {
100                        ty: Types::ByteStr,
101                        optional: false,
102                    })
103                } else if p.path == ident_vec_path {
104                    Ok(Type {
105                        ty: Types::Idents,
106                        optional: false,
107                    })
108                } else if p.path.is_ident("u8") {
109                    Ok(Type {
110                        ty: Types::Byte,
111                        optional: false,
112                    })
113                } else if p.path.is_ident("char") {
114                    Ok(Type {
115                        ty: Types::Char,
116                        optional: false,
117                    })
118                } else if p.path.is_ident("i32") {
119                    Ok(Type {
120                        ty: Types::I32,
121                        optional: false,
122                    })
123                } else if p.path.is_ident("f32") {
124                    Ok(Type {
125                        ty: Types::F32,
126                        optional: false,
127                    })
128                } else if p.path.is_ident("bool") {
129                    Ok(Type {
130                        ty: Types::Bool,
131                        optional: false,
132                    })
133                } else {
134                    if p.path.segments.len() == 1 {
135                        let segment = p.path.segments.first().unwrap();
136                        if segment.ident == "Option" {
137                            if let PathArguments::AngleBracketed(args) = &segment.arguments {
138                                if let Some(GenericArgument::Type(ty)) = args.args.first() {
139                                    return Type::try_from(ty).and_then(|ty| {
140                                        if ty.optional {
141                                            error()
142                                        } else {
143                                            Ok(Type {
144                                                ty: ty.ty,
145                                                optional: true,
146                                            })
147                                        }
148                                    });
149                                }
150                            }
151                        }
152                    }
153                    error()
154                }
155            }
156            syn::Type::Tuple(t) if t.elems.is_empty() => Ok(Type {
157                ty: Types::Flag,
158                optional: false,
159            }),
160            _ => error(),
161        }
162    }
163}