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 { parse(input: ParseStream<'_>) -> Result<Self>27 fn parse(input: ParseStream<'_>) -> Result<Self> { 28 let attribute_items = input.parse_terminated(AttributeItem::parse, syn::Token![,])?; 29 let mut maybe_error_type = None; 30 for attribute_item in &attribute_items { 31 match attribute_item { 32 AttributeItem::ErrorType(error_type) => { 33 if maybe_error_type.is_some() { 34 return Err(Error::new( 35 error_type.span, 36 "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 { parse(input: ParseStream<'_>) -> Result<Self>50 fn parse(input: ParseStream<'_>) -> Result<Self> { 51 let lookahead = input.lookahead1(); 52 if lookahead.peek(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 { parse(input: ParseStream) -> Result<Self>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 { parse(input: ParseStream<'_>) -> Result<Self>127 fn parse(input: ParseStream<'_>) -> Result<Self> { 128 let lookahead = input.lookahead1(); 129 if lookahead.peek(kw::name) { 130 input.parse().map(Self::Name) 131 } else if lookahead.peek(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 { parse(input: ParseStream) -> Result<Self>147 fn parse(input: ParseStream) -> Result<Self> { 148 input.parse::<kw::name>()?; 149 input.parse::<syn::Token![=]>()?; 150 let 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 { parse(input: ParseStream) -> Result<Self>163 fn parse(input: ParseStream) -> Result<Self> { 164 input.parse::<kw::constructor>()?; 165 input.parse::<syn::Token![=]>()?; 166 let 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] parse_num_enum_attr()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] parse_num_enum_attr_swapped_order()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] missing_constructor()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] missing_name()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] missing_both()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] extra_attr()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] multiple_names()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] multiple_constructors()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