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)]
11pub struct Type {
13 pub ty: Types,
15 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)]
58pub enum Types {
60 Any,
62 Flag,
64 Str,
66 ByteStr,
68 Byte,
70 Char,
72 I32,
74 F32,
76 Bool,
78 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}