1use crate::utils::die;
2use proc_macro2::Span;
3use syn::{
4 parse::{Parse, ParseStream},
5 Error, Result,
6};
7
8mod 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))]
16pub(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))]
22pub(crate) enum AttributeItem {
23 ErrorType(ErrorTypeAttribute),
24}
25
26impl 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
49impl 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))]
63pub(crate) struct ErrorTypeAttribute {
64 pub(crate) name: ErrorTypeNameAttribute,
65 pub(crate) constructor: ErrorTypeConstructorAttribute,
66
67 span: Span,
68}
69
70impl 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
121pub(crate) enum ErrorTypeAttributeNamedArgument {
122 Name(ErrorTypeNameAttribute),
123 Constructor(ErrorTypeConstructorAttribute),
124}
125
126impl 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))]
142pub(crate) struct ErrorTypeNameAttribute {
143 pub(crate) path: syn::Path,
144}
145
146impl 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))]
158pub(crate) struct ErrorTypeConstructorAttribute {
159 pub(crate) path: syn::Path,
160}
161
162impl 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)]
172mod 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