1 | use crate::utils::die; |
2 | use proc_macro2::Span; |
3 | use syn::{ |
4 | parse::{Parse, ParseStream}, |
5 | Error, Result, |
6 | }; |
7 | |
8 | mod kw { |
9 | syn::custom_keyword!(constructor); |
10 | syn::custom_keyword!(error_type); |
11 | syn::custom_keyword!(name); |
12 | } |
13 | |
14 | // Example: error_type(name = Foo, constructor = Foo::new) |
15 | #[cfg_attr (test, derive(Debug))] |
16 | pub(crate) struct Attributes { |
17 | pub(crate) error_type: Option<ErrorTypeAttribute>, |
18 | } |
19 | |
20 | // Example: error_type(name = Foo, constructor = Foo::new) |
21 | #[cfg_attr (test, derive(Debug))] |
22 | pub(crate) enum AttributeItem { |
23 | ErrorType(ErrorTypeAttribute), |
24 | } |
25 | |
26 | impl Parse for Attributes { |
27 | fn parse(input: ParseStream<'_>) -> Result<Self> { |
28 | let attribute_items: Punctuated = input.parse_terminated(parser:AttributeItem::parse, separator:syn::Token![,])?; |
29 | let mut maybe_error_type: Option = None; |
30 | for attribute_item: &AttributeItem in &attribute_items { |
31 | match attribute_item { |
32 | AttributeItem::ErrorType(error_type: &ErrorTypeAttribute) => { |
33 | if maybe_error_type.is_some() { |
34 | return Err(Error::new( |
35 | error_type.span, |
36 | message:"num_enum attribute must have at most one error_type" , |
37 | )); |
38 | } |
39 | maybe_error_type = Some(error_type.clone()); |
40 | } |
41 | } |
42 | } |
43 | Ok(Self { |
44 | error_type: maybe_error_type, |
45 | }) |
46 | } |
47 | } |
48 | |
49 | impl Parse for AttributeItem { |
50 | fn parse(input: ParseStream<'_>) -> Result<Self> { |
51 | let lookahead: Lookahead1<'_> = input.lookahead1(); |
52 | if lookahead.peek(token:kw::error_type) { |
53 | input.parse().map(Self::ErrorType) |
54 | } else { |
55 | Err(lookahead.error()) |
56 | } |
57 | } |
58 | } |
59 | |
60 | // Example: error_type(name = Foo, constructor = Foo::new) |
61 | #[derive (Clone)] |
62 | #[cfg_attr (test, derive(Debug))] |
63 | pub(crate) struct ErrorTypeAttribute { |
64 | pub(crate) name: ErrorTypeNameAttribute, |
65 | pub(crate) constructor: ErrorTypeConstructorAttribute, |
66 | |
67 | span: Span, |
68 | } |
69 | |
70 | impl Parse for ErrorTypeAttribute { |
71 | fn parse(input: ParseStream) -> Result<Self> { |
72 | let keyword: kw::error_type = input.parse()?; |
73 | let span = keyword.span; |
74 | let content; |
75 | syn::parenthesized!(content in input); |
76 | let attribute_values = |
77 | content.parse_terminated(ErrorTypeAttributeNamedArgument::parse, syn::Token![,])?; |
78 | let mut name = None; |
79 | let mut constructor = None; |
80 | for attribute_value in &attribute_values { |
81 | match attribute_value { |
82 | ErrorTypeAttributeNamedArgument::Name(name_attr) => { |
83 | if name.is_some() { |
84 | die!("num_enum error_type attribute must have exactly one `name` value" ); |
85 | } |
86 | name = Some(name_attr.clone()); |
87 | } |
88 | ErrorTypeAttributeNamedArgument::Constructor(constructor_attr) => { |
89 | if constructor.is_some() { |
90 | die!("num_enum error_type attribute must have exactly one `constructor` value" ) |
91 | } |
92 | constructor = Some(constructor_attr.clone()); |
93 | } |
94 | } |
95 | } |
96 | match (name, constructor) { |
97 | (None, None) => Err(Error::new( |
98 | span, |
99 | "num_enum error_type attribute requires `name` and `constructor` values" , |
100 | )), |
101 | (Some(_), None) => Err(Error::new( |
102 | span, |
103 | "num_enum error_type attribute requires `constructor` value" , |
104 | )), |
105 | (None, Some(_)) => Err(Error::new( |
106 | span, |
107 | "num_enum error_type attribute requires `name` value" , |
108 | )), |
109 | (Some(name), Some(constructor)) => Ok(Self { |
110 | name, |
111 | constructor, |
112 | span, |
113 | }), |
114 | } |
115 | } |
116 | } |
117 | |
118 | // Examples: |
119 | // * name = Foo |
120 | // * constructor = Foo::new |
121 | pub(crate) enum ErrorTypeAttributeNamedArgument { |
122 | Name(ErrorTypeNameAttribute), |
123 | Constructor(ErrorTypeConstructorAttribute), |
124 | } |
125 | |
126 | impl Parse for ErrorTypeAttributeNamedArgument { |
127 | fn parse(input: ParseStream<'_>) -> Result<Self> { |
128 | let lookahead: Lookahead1<'_> = input.lookahead1(); |
129 | if lookahead.peek(token:kw::name) { |
130 | input.parse().map(Self::Name) |
131 | } else if lookahead.peek(token:kw::constructor) { |
132 | input.parse().map(Self::Constructor) |
133 | } else { |
134 | Err(lookahead.error()) |
135 | } |
136 | } |
137 | } |
138 | |
139 | // Example: name = Foo |
140 | #[derive (Clone)] |
141 | #[cfg_attr (test, derive(Debug))] |
142 | pub(crate) struct ErrorTypeNameAttribute { |
143 | pub(crate) path: syn::Path, |
144 | } |
145 | |
146 | impl Parse for ErrorTypeNameAttribute { |
147 | fn parse(input: ParseStream) -> Result<Self> { |
148 | input.parse::<kw::name>()?; |
149 | input.parse::<syn::Token![=]>()?; |
150 | let path: Path = input.parse()?; |
151 | Ok(Self { path }) |
152 | } |
153 | } |
154 | |
155 | // Example: constructor = Foo::new |
156 | #[derive (Clone)] |
157 | #[cfg_attr (test, derive(Debug))] |
158 | pub(crate) struct ErrorTypeConstructorAttribute { |
159 | pub(crate) path: syn::Path, |
160 | } |
161 | |
162 | impl Parse for ErrorTypeConstructorAttribute { |
163 | fn parse(input: ParseStream) -> Result<Self> { |
164 | input.parse::<kw::constructor>()?; |
165 | input.parse::<syn::Token![=]>()?; |
166 | let path: Path = input.parse()?; |
167 | Ok(Self { path }) |
168 | } |
169 | } |
170 | |
171 | #[cfg (test)] |
172 | mod test { |
173 | use crate::enum_attributes::Attributes; |
174 | use quote::ToTokens; |
175 | use syn::{parse_quote, Path}; |
176 | |
177 | #[test ] |
178 | fn parse_num_enum_attr() { |
179 | let expected_name: Path = parse_quote! { Foo }; |
180 | let expected_constructor: Path = parse_quote! { ::foo::Foo::<u8>::new }; |
181 | |
182 | let attributes: Attributes = |
183 | syn::parse_str("error_type(name = Foo, constructor = ::foo::Foo::<u8>::new)" ).unwrap(); |
184 | assert!(attributes.error_type.is_some()); |
185 | let error_type = attributes.error_type.unwrap(); |
186 | assert_eq!( |
187 | error_type.name.path.to_token_stream().to_string(), |
188 | expected_name.to_token_stream().to_string() |
189 | ); |
190 | assert_eq!( |
191 | error_type.constructor.path.to_token_stream().to_string(), |
192 | expected_constructor.to_token_stream().to_string() |
193 | ); |
194 | } |
195 | |
196 | #[test ] |
197 | fn parse_num_enum_attr_swapped_order() { |
198 | let expected_name: Path = parse_quote! { Foo }; |
199 | let expected_constructor: Path = parse_quote! { ::foo::Foo::<u8>::new }; |
200 | |
201 | let attributes: Attributes = |
202 | syn::parse_str("error_type(constructor = ::foo::Foo::<u8>::new, name = Foo)" ).unwrap(); |
203 | assert!(attributes.error_type.is_some()); |
204 | let error_type = attributes.error_type.unwrap(); |
205 | assert_eq!( |
206 | error_type.name.path.to_token_stream().to_string(), |
207 | expected_name.to_token_stream().to_string() |
208 | ); |
209 | assert_eq!( |
210 | error_type.constructor.path.to_token_stream().to_string(), |
211 | expected_constructor.to_token_stream().to_string() |
212 | ); |
213 | } |
214 | |
215 | #[test ] |
216 | fn missing_constructor() { |
217 | let err = syn::parse_str::<Attributes>("error_type(name = Foo)" ).unwrap_err(); |
218 | assert_eq!( |
219 | err.to_string(), |
220 | "num_enum error_type attribute requires `constructor` value" |
221 | ); |
222 | } |
223 | |
224 | #[test ] |
225 | fn missing_name() { |
226 | let err = syn::parse_str::<Attributes>("error_type(constructor = Foo::new)" ).unwrap_err(); |
227 | assert_eq!( |
228 | err.to_string(), |
229 | "num_enum error_type attribute requires `name` value" |
230 | ); |
231 | } |
232 | |
233 | #[test ] |
234 | fn missing_both() { |
235 | let err = syn::parse_str::<Attributes>("error_type()" ).unwrap_err(); |
236 | assert_eq!( |
237 | err.to_string(), |
238 | "num_enum error_type attribute requires `name` and `constructor` values" |
239 | ); |
240 | } |
241 | |
242 | #[test ] |
243 | fn extra_attr() { |
244 | let err = syn::parse_str::<Attributes>( |
245 | "error_type(name = Foo, constructor = Foo::new, extra = unneeded)" , |
246 | ) |
247 | .unwrap_err(); |
248 | assert_eq!(err.to_string(), "expected `name` or `constructor`" ); |
249 | } |
250 | |
251 | #[test ] |
252 | fn multiple_names() { |
253 | let err = syn::parse_str::<Attributes>( |
254 | "error_type(name = Foo, name = Foo, constructor = Foo::new)" , |
255 | ) |
256 | .unwrap_err(); |
257 | assert_eq!( |
258 | err.to_string(), |
259 | "num_enum error_type attribute must have exactly one `name` value" |
260 | ); |
261 | } |
262 | |
263 | #[test ] |
264 | fn multiple_constructors() { |
265 | let err = syn::parse_str::<Attributes>( |
266 | "error_type(name = Foo, constructor = Foo::new, constructor = Foo::new)" , |
267 | ) |
268 | .unwrap_err(); |
269 | assert_eq!( |
270 | err.to_string(), |
271 | "num_enum error_type attribute must have exactly one `constructor` value" |
272 | ); |
273 | } |
274 | } |
275 | |