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: &Setting| *s == setting)
67 }
68
69 pub(crate) fn set(&mut self, setting: Setting) {
70 self.0.push(setting)
71 }
72}
73