| 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 | |