1#[macro_use]
2extern crate quote;
3
4use proc_macro::TokenStream;
5use regex::Regex;
6use syn::{
7 parse_macro_input, Attribute, Data, DeriveInput, Expr, Fields, ItemEnum, ItemFn, ItemStruct, Meta,
8 NestedMeta, Stmt,
9};
10
11#[proc_macro_attribute]
12pub fn bot(_attr: TokenStream, item: TokenStream) -> TokenStream {
13 let item = parse_macro_input!(item as ItemStruct);
15
16 let name = item.ident;
17 let vis = item.vis;
18 let attrs = item.attrs;
19 let generics = item.generics;
20 let fields = match item.fields {
21 Fields::Named(named_fields) => {
22 let named = named_fields.named;
23 quote! {#named}
24 }
25 Fields::Unnamed(_) => panic!("#[bot] is not allowed for tuple structs"),
26 unit => quote! {#unit},
27 };
28
29 TokenStream::from(quote! {
30 #(#attrs)*
31 #vis struct #name#generics {
32 _bot: rust_sc2::bot::Bot,
33 #fields
34 }
35 impl std::ops::Deref for #name {
36 type Target = rust_sc2::bot::Bot;
37
38 fn deref(&self) -> &Self::Target {
39 &self._bot
40 }
41 }
42 impl std::ops::DerefMut for #name {
43 fn deref_mut(&mut self) -> &mut Self::Target {
44 &mut self._bot
45 }
46 }
47 })
48}
49
50#[proc_macro_attribute]
51pub fn bot_new(_attr: TokenStream, item: TokenStream) -> TokenStream {
52 let item = parse_macro_input!(item as ItemFn);
54
55 let vis = item.vis;
56 let signature = item.sig;
57 let blocks = item.block.stmts.iter().map(|s| match s {
58 Stmt::Expr(expr) => match expr {
59 Expr::Struct(struct_expr) => {
60 let path = &struct_expr.path;
61 let rest = match &struct_expr.rest {
62 Some(expr) => quote! {#expr},
63 None => quote! {},
64 };
65 let fields = struct_expr.fields.iter();
66
67 quote! {
68 #path {
69 _bot: Default::default(),
70 #(#fields,)*
71 ..#rest
72 }
73 }
74 }
75 n => quote! {#n},
76 },
77 n => quote! {#n},
78 });
79
80 TokenStream::from(quote! {
81 #vis #signature {
82 #(#blocks)*
83 }
84 })
85}
86
87#[proc_macro_derive(FromStr, attributes(enum_from_str))]
88pub fn enum_from_str_derive(input: TokenStream) -> TokenStream {
89 let item = parse_macro_input!(input as DeriveInput);
90 if let Data::Enum(data) = item.data {
91 let name = item.ident;
92 let variants = data.variants.iter().map(|v| &v.ident);
93 let additional_attributes = |a: &Attribute| {
96 if a.path.is_ident("enum_from_str") {
97 if let Meta::List(list) = a.parse_meta().unwrap() {
98 return list.nested.iter().any(|n| {
99 if let NestedMeta::Meta(Meta::Path(path)) = n {
100 path.is_ident("use_primitives")
101 } else {
102 false
103 }
104 });
105 } else {
106 unreachable!("No options found in attribute `enum_from_str`")
107 }
108 }
109 false
110 };
111 let other_cases = if item.attrs.iter().any(additional_attributes) {
112 quote! {
113 n => {
114 if let Ok(num) = n.parse() {
115 if let Some(result) = Self::from_i64(num) {
116 return Ok(result);
117 }
118 }
119 return Err(sc2_macro::ParseEnumError);
120 }
121 }
122 } else {
123 quote! {_ => return Err(sc2_macro::ParseEnumError)}
124 };
125 TokenStream::from(quote! {
126 impl std::str::FromStr for #name {
127 type Err = sc2_macro::ParseEnumError;
128
129 fn from_str(s: &str) -> Result<Self, Self::Err> {
130 Ok(match s {
131 #(
132 stringify!(#variants) => Self::#variants,
133 )*
136 #other_cases,
137 })
138 }
139 }
140 })
141 } else {
142 panic!("Can only derive FromStr for enums")
143 }
144}
145
146#[proc_macro_attribute]
147pub fn variant_checkers(_attr: TokenStream, item: TokenStream) -> TokenStream {
148 let item = parse_macro_input!(item as ItemEnum);
150
151 let name = &item.ident;
152 let variants = item.variants.iter().map(|v| &v.ident);
153 let re = Regex::new(r"[A-Z0-9]{1}[a-z0-9]*").unwrap();
154 let snake_variants = variants.clone().map(|v| {
155 format_ident!(
156 "is_{}",
157 re.find_iter(&v.to_string())
158 .map(|m| m.as_str().to_ascii_lowercase())
159 .collect::<Vec<String>>()
160 .join("_")
161 )
162 });
163
164 TokenStream::from(quote! {
165 #item
166 impl #name {
167 #(
168 #[inline]
169 pub fn #snake_variants(self) -> bool {
170 matches!(self, Self::#variants)
171 }
172 )*
173 }
174 })
175}