1 use proc_macro2::{Span, TokenStream}; 2 use syn::{ 3 parenthesized, 4 parse::{Parse, ParseStream}, 5 parse2, parse_str, 6 punctuated::Punctuated, 7 spanned::Spanned, 8 Attribute, DeriveInput, Ident, LitBool, LitStr, Path, Token, Variant, Visibility, 9 }; 10 11 use super::case_style::CaseStyle; 12 13 pub mod kw { 14 use syn::custom_keyword; 15 pub use syn::token::Crate; 16 17 // enum metadata 18 custom_keyword!(serialize_all); 19 20 // enum discriminant metadata 21 custom_keyword!(derive); 22 custom_keyword!(name); 23 custom_keyword!(vis); 24 25 // variant metadata 26 custom_keyword!(message); 27 custom_keyword!(detailed_message); 28 custom_keyword!(serialize); 29 custom_keyword!(to_string); 30 custom_keyword!(disabled); 31 custom_keyword!(default); 32 custom_keyword!(props); 33 custom_keyword!(ascii_case_insensitive); 34 } 35 36 pub enum EnumMeta { 37 SerializeAll { 38 kw: kw::serialize_all, 39 case_style: CaseStyle, 40 }, 41 AsciiCaseInsensitive(kw::ascii_case_insensitive), 42 Crate { 43 kw: kw::Crate, 44 crate_module_path: Path, 45 }, 46 } 47 48 impl Parse for EnumMeta { 49 fn parse(input: ParseStream) -> syn::Result<Self> { 50 let lookahead = input.lookahead1(); 51 if lookahead.peek(kw::serialize_all) { 52 let kw = input.parse::<kw::serialize_all>()?; 53 input.parse::<Token![=]>()?; 54 let case_style = input.parse()?; 55 Ok(EnumMeta::SerializeAll { kw, case_style }) 56 } else if lookahead.peek(kw::Crate) { 57 let kw = input.parse::<kw::Crate>()?; 58 input.parse::<Token![=]>()?; 59 let path_str: LitStr = input.parse()?; 60 let path_tokens = parse_str(&path_str.value())?; 61 let crate_module_path = parse2(path_tokens)?; 62 Ok(EnumMeta::Crate { 63 kw, 64 crate_module_path, 65 }) 66 } else if lookahead.peek(kw::ascii_case_insensitive) { 67 let kw = input.parse()?; 68 Ok(EnumMeta::AsciiCaseInsensitive(kw)) 69 } else { 70 Err(lookahead.error()) 71 } 72 } 73 } 74 75 impl Spanned for EnumMeta { 76 fn span(&self) -> Span { 77 match self { 78 EnumMeta::SerializeAll { kw, .. } => kw.span(), 79 EnumMeta::AsciiCaseInsensitive(kw) => kw.span(), 80 EnumMeta::Crate { kw, .. } => kw.span(), 81 } 82 } 83 } 84 85 pub enum EnumDiscriminantsMeta { 86 Derive { kw: kw::derive, paths: Vec<Path> }, 87 Name { kw: kw::name, name: Ident }, 88 Vis { kw: kw::vis, vis: Visibility }, 89 Other { path: Path, nested: TokenStream }, 90 } 91 92 impl Parse for EnumDiscriminantsMeta { 93 fn parse(input: ParseStream) -> syn::Result<Self> { 94 if input.peek(kw::derive) { 95 let kw = input.parse()?; 96 let content; 97 parenthesized!(content in input); 98 let paths = content.parse_terminated::<_, Token![,]>(Path::parse)?; 99 Ok(EnumDiscriminantsMeta::Derive { 100 kw, 101 paths: paths.into_iter().collect(), 102 }) 103 } else if input.peek(kw::name) { 104 let kw = input.parse()?; 105 let content; 106 parenthesized!(content in input); 107 let name = content.parse()?; 108 Ok(EnumDiscriminantsMeta::Name { kw, name }) 109 } else if input.peek(kw::vis) { 110 let kw = input.parse()?; 111 let content; 112 parenthesized!(content in input); 113 let vis = content.parse()?; 114 Ok(EnumDiscriminantsMeta::Vis { kw, vis }) 115 } else { 116 let path = input.parse()?; 117 let content; 118 parenthesized!(content in input); 119 let nested = content.parse()?; 120 Ok(EnumDiscriminantsMeta::Other { path, nested }) 121 } 122 } 123 } 124 125 impl Spanned for EnumDiscriminantsMeta { 126 fn span(&self) -> Span { 127 match self { 128 EnumDiscriminantsMeta::Derive { kw, .. } => kw.span, 129 EnumDiscriminantsMeta::Name { kw, .. } => kw.span, 130 EnumDiscriminantsMeta::Vis { kw, .. } => kw.span, 131 EnumDiscriminantsMeta::Other { path, .. } => path.span(), 132 } 133 } 134 } 135 136 pub trait DeriveInputExt { 137 /// Get all the strum metadata associated with an enum. 138 fn get_metadata(&self) -> syn::Result<Vec<EnumMeta>>; 139 140 /// Get all the strum_discriminants metadata associated with an enum. 141 fn get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>>; 142 } 143 144 impl DeriveInputExt for DeriveInput { 145 fn get_metadata(&self) -> syn::Result<Vec<EnumMeta>> { 146 get_metadata_inner("strum", &self.attrs) 147 } 148 149 fn get_discriminants_metadata(&self) -> syn::Result<Vec<EnumDiscriminantsMeta>> { 150 get_metadata_inner("strum_discriminants", &self.attrs) 151 } 152 } 153 154 pub enum VariantMeta { 155 Message { 156 kw: kw::message, 157 value: LitStr, 158 }, 159 DetailedMessage { 160 kw: kw::detailed_message, 161 value: LitStr, 162 }, 163 Serialize { 164 kw: kw::serialize, 165 value: LitStr, 166 }, 167 ToString { 168 kw: kw::to_string, 169 value: LitStr, 170 }, 171 Disabled(kw::disabled), 172 Default(kw::default), 173 AsciiCaseInsensitive { 174 kw: kw::ascii_case_insensitive, 175 value: bool, 176 }, 177 Props { 178 kw: kw::props, 179 props: Vec<(LitStr, LitStr)>, 180 }, 181 } 182 183 impl Parse for VariantMeta { 184 fn parse(input: ParseStream) -> syn::Result<Self> { 185 let lookahead = input.lookahead1(); 186 if lookahead.peek(kw::message) { 187 let kw = input.parse()?; 188 let _: Token![=] = input.parse()?; 189 let value = input.parse()?; 190 Ok(VariantMeta::Message { kw, value }) 191 } else if lookahead.peek(kw::detailed_message) { 192 let kw = input.parse()?; 193 let _: Token![=] = input.parse()?; 194 let value = input.parse()?; 195 Ok(VariantMeta::DetailedMessage { kw, value }) 196 } else if lookahead.peek(kw::serialize) { 197 let kw = input.parse()?; 198 let _: Token![=] = input.parse()?; 199 let value = input.parse()?; 200 Ok(VariantMeta::Serialize { kw, value }) 201 } else if lookahead.peek(kw::to_string) { 202 let kw = input.parse()?; 203 let _: Token![=] = input.parse()?; 204 let value = input.parse()?; 205 Ok(VariantMeta::ToString { kw, value }) 206 } else if lookahead.peek(kw::disabled) { 207 Ok(VariantMeta::Disabled(input.parse()?)) 208 } else if lookahead.peek(kw::default) { 209 Ok(VariantMeta::Default(input.parse()?)) 210 } else if lookahead.peek(kw::ascii_case_insensitive) { 211 let kw = input.parse()?; 212 let value = if input.peek(Token![=]) { 213 let _: Token![=] = input.parse()?; 214 input.parse::<LitBool>()?.value 215 } else { 216 true 217 }; 218 Ok(VariantMeta::AsciiCaseInsensitive { kw, value }) 219 } else if lookahead.peek(kw::props) { 220 let kw = input.parse()?; 221 let content; 222 parenthesized!(content in input); 223 let props = content.parse_terminated::<_, Token![,]>(Prop::parse)?; 224 Ok(VariantMeta::Props { 225 kw, 226 props: props 227 .into_iter() 228 .map(|Prop(k, v)| (LitStr::new(&k.to_string(), k.span()), v)) 229 .collect(), 230 }) 231 } else { 232 Err(lookahead.error()) 233 } 234 } 235 } 236 237 struct Prop(Ident, LitStr); 238 239 impl Parse for Prop { 240 fn parse(input: ParseStream) -> syn::Result<Self> { 241 use syn::ext::IdentExt; 242 243 let k = Ident::parse_any(&input)?; 244 let _: Token![=] = input.parse()?; 245 let v = input.parse()?; 246 247 Ok(Prop(k, v)) 248 } 249 } 250 251 impl Spanned for VariantMeta { 252 fn span(&self) -> Span { 253 match self { 254 VariantMeta::Message { kw, .. } => kw.span, 255 VariantMeta::DetailedMessage { kw, .. } => kw.span, 256 VariantMeta::Serialize { kw, .. } => kw.span, 257 VariantMeta::ToString { kw, .. } => kw.span, 258 VariantMeta::Disabled(kw) => kw.span, 259 VariantMeta::Default(kw) => kw.span, 260 VariantMeta::AsciiCaseInsensitive { kw, .. } => kw.span, 261 VariantMeta::Props { kw, .. } => kw.span, 262 } 263 } 264 } 265 266 pub trait VariantExt { 267 /// Get all the metadata associated with an enum variant. 268 fn get_metadata(&self) -> syn::Result<Vec<VariantMeta>>; 269 } 270 271 impl VariantExt for Variant { 272 fn get_metadata(&self) -> syn::Result<Vec<VariantMeta>> { 273 get_metadata_inner("strum", &self.attrs) 274 } 275 } 276 277 fn get_metadata_inner<'a, T: Parse + Spanned>( 278 ident: &str, 279 it: impl IntoIterator<Item = &'a Attribute>, 280 ) -> syn::Result<Vec<T>> { 281 it.into_iter() 282 .filter(|attr| attr.path.is_ident(ident)) 283 .try_fold(Vec::new(), |mut vec, attr| { 284 vec.extend(attr.parse_args_with(Punctuated::<T, Token![,]>::parse_terminated)?); 285 Ok(vec) 286 }) 287 } 288