1use crate::{Error, Result}; 2use proc_macro2::{Ident, Span, TokenStream, TokenTree}; 3 4macro_rules! decl_settings { 5 ($($val:expr => $variant:ident),+ $(,)*) => { 6 #[derive(PartialEq)] 7 pub(crate) enum Setting { 8 $($variant),* 9 } 10 11 fn ident_to_setting(ident: Ident) -> Result<Setting> { 12 match &*ident.to_string() { 13 $($val => Ok(Setting::$variant),)* 14 _ => { 15 let possible_vals = [$($val),*] 16 .iter() 17 .map(|v| format!("`{}`", v)) 18 .collect::<Vec<_>>() 19 .join(", "); 20 21 Err(Error::new( 22 ident.span(), 23 format!("unknown setting `{}`, expected one of {}", ident, possible_vals))) 24 } 25 } 26 } 27 }; 28} 29 30decl_settings! { 31 "assert_unwind_safe" => AssertUnwindSafe, 32 "allow_not_macro" => AllowNotMacro, 33 "proc_macro_hack" => ProcMacroHack, 34} 35 36pub(crate) fn parse_settings(input: TokenStream) -> Result<Settings> { 37 let mut input = input.into_iter(); 38 let mut res = Settings(Vec::new()); 39 loop { 40 match input.next() { 41 Some(TokenTree::Ident(ident)) => { 42 res.0.push(ident_to_setting(ident)?); 43 } 44 None => return Ok(res), 45 other => { 46 let span = other.map_or(Span::call_site(), |tt| tt.span()); 47 return Err(Error::new(span, "expected identifier".to_string())); 48 } 49 } 50 51 match input.next() { 52 Some(TokenTree::Punct(ref punct)) if punct.as_char() == ',' => {} 53 None => return Ok(res), 54 other => { 55 let span = other.map_or(Span::call_site(), |tt| tt.span()); 56 return Err(Error::new(span, "expected `,`".to_string())); 57 } 58 } 59 } 60} 61 62pub(crate) struct Settings(Vec<Setting>); 63 64impl Settings { 65 pub(crate) fn is_set(&self, setting: Setting) -> bool { 66 self.0.iter().any(|s| *s == setting) 67 } 68 69 pub(crate) fn set(&mut self, setting: Setting) { 70 self.0.push(setting) 71 } 72} 73