1use proc_macro::TokenStream;
2use quote::quote;
3
4struct FlagSets {
5 d: bool,
6 i: bool,
7 g: bool,
8 s: bool,
9 m: bool,
10 y: bool,
11 u: bool,
12 v: bool,
13}
14fn new_empty_flagsets() -> FlagSets {
15 FlagSets {
16 d: false,
17 i: false,
18 g: false,
19 s: false,
20 m: false,
21 y: false,
22 u: false,
23 v: false,
24 }
25}
26fn check_flags(source: &str) -> Result<(), char> {
27 let mut flags = new_empty_flagsets();
28 let mut inner = |ch: char| -> Option<()> {
29 match ch {
30 'd' => (!flags.d).then(|| flags.d = true)?,
31 'i' => (!flags.i).then(|| flags.i = true)?,
32 'g' => (!flags.g).then(|| flags.g = true)?,
33 's' => (!flags.s).then(|| flags.s = true)?,
34 'm' => (!flags.m).then(|| flags.m = true)?,
35 'y' => (!flags.y).then(|| flags.y = true)?,
36 'u' => (!flags.u && !flags.v).then(|| flags.u = true)?,
37 'v' => (!flags.u && !flags.v).then(|| flags.v = true)?,
38 _ => return None,
39 };
40 Some(())
41 };
42 for ch in source.chars() {
43 if inner(ch).is_none() {
44 return Err(ch);
45 }
46 }
47 Ok(())
48}
49
50#[proc_macro]
53pub fn flags(item: TokenStream) -> TokenStream {
54 let item = syn::parse_macro_input!(item as syn::Lit);
55 let item = match item {
56 syn::Lit::Str(v) => v,
57 _ => panic!("Not a string literal"),
58 };
59 let value = item.value();
60 if let Err(e) = check_flags(&value) {
61 panic!("Invalid flags at char: '{}'", e);
62 }
63 quote!(::js_regexp::Flags::new_unchecked(#item)).into()
64}
65
66#[derive(Clone, Copy, Debug)]
67enum Mode {
68 Skip,
69 Do,
70}
71
72#[proc_macro_derive(EnumConvert, attributes(enum_convert))]
73pub fn derive(input: TokenStream) -> TokenStream {
74 let input = syn::parse_macro_input!(input as syn::ItemEnum);
75 let enum_ident = input.ident;
76 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
77 let mut output = proc_macro::TokenStream::new();
78
79 let outer_attr = input
80 .attrs
81 .iter()
82 .find(|v| v.path().is_ident("enum_convert"));
83 let mut into_mode: Mode = Mode::Skip;
84 let mut from_mode: Mode = Mode::Skip;
85 if let Some(attr) = outer_attr {
86 let _ = attr.parse_nested_meta(|meta| {
87 if meta.path.is_ident("into") {
88 into_mode = Mode::Do;
89 } else if meta.path.is_ident("from") {
90 from_mode = Mode::Do;
91 }
92 Ok(())
93 });
94 }
95
96 for variant in input.variants {
97 let variant_ident = variant.ident;
98 let type_path = match variant.fields {
99 syn::Fields::Unnamed(v) => match v.unnamed.first() {
100 Some(v) => match &v.ty {
101 syn::Type::Path(v) => v.to_owned(),
102 _ => continue,
103 },
104 None => continue,
105 },
106 _ => continue,
107 };
108
109 let mut into_override = into_mode;
110 let mut from_override = from_mode;
111 let inner_attr = variant
112 .attrs
113 .iter()
114 .find(|v| v.path().is_ident("enum_convert"));
115 if let Some(attr) = inner_attr {
116 into_override = Mode::Skip;
117 from_override = Mode::Skip;
118 attr.parse_nested_meta(|meta| {
119 if meta.path.is_ident("skip") {
120 } else if meta.path.is_ident("from") {
121 from_override = Mode::Do;
122 } else if meta.path.is_ident("into") {
123 into_override = Mode::Do;
124 } else {
125 return Err(meta.error("Invalid mode override"));
126 }
127 Ok(())
128 })
129 .unwrap()
130 }
131
132 if let Mode::Do = from_override {
133 output.extend(Into::<proc_macro::TokenStream>::into(quote! {
134 impl #impl_generics From<#type_path> for #enum_ident #ty_generics #where_clause {
135 fn from(value: #type_path) -> Self {
136 Self::#variant_ident(value)
137 }
138 }
139 }))
140 }
141 if let Mode::Do = into_override {
142 output.extend(Into::<proc_macro::TokenStream>::into(quote! {
143 impl #impl_generics TryInto<#type_path> for #enum_ident #ty_generics #where_clause {
144 type Error = &'static str;
145 fn try_into(self) -> Result<#type_path, Self::Error> {
146 match self {
147 Self::#variant_ident(value) => Ok(value),
148 _ => Err("Incorrect Variant"),
149 }
150 }
151 }
152 }));
153 }
154 }
155 output.into()
156}