1#![no_std]
5
6extern crate alloc;
7
8use alloc::format;
9use alloc::string::ToString;
10use proc_macro::TokenStream;
11
12use irox_derive_helpers::DeriveMethods;
13use syn::spanned::Spanned;
14use syn::{parse_macro_input, Data, DeriveInput, Error};
15
16fn compile_error<T: Spanned>(span: &T, msg: &'static str) -> TokenStream {
17 Error::new(span.span(), msg).into_compile_error().into()
18}
19
20#[proc_macro_derive(EnumName)]
21pub fn enumname_derive(input: TokenStream) -> TokenStream {
22 let input = parse_macro_input!(input as DeriveInput);
23 let Data::Enum(s) = input.data else {
24 return compile_error(&input, "Unsupported non-struct type found");
25 };
26 let mut match_elements = alloc::vec::Vec::<TokenStream>::new();
27 let mut name_literals = alloc::vec::Vec::<TokenStream>::new();
28
29 for field in s.variants {
30 let field_ident = field.ident;
32 let field_name = field_ident.to_string();
33
34 name_literals.push({
35 let mut ts = TokenStream::create_literal(&field_name);
36 ts.add_punc(',');
37 ts
38 });
39 match_elements.push(if field.fields.is_empty() {
40 let mut ts = TokenStream::new();
41 ts.append_match_item(
42 TokenStream::create_path(&["Self", &field_name]),
43 TokenStream::create_literal(&field_name),
44 );
45 ts
46 } else {
47 let mut ts = TokenStream::new();
48 ts.append_match_item(
49 {
50 let mut ts = TokenStream::create_path(&["Self", &field_name]);
51 ts.add_parens(TokenStream::create_punct2('.', '.'));
52 ts
53 },
54 TokenStream::create_literal(&field_name),
55 );
56 ts
57 });
58 }
59
60 let enum_name = input.ident;
61 let mut out = TokenStream::new();
62 out.add_ident("impl");
63 out.add_ident(&enum_name.to_string());
64 out.wrap_braces({
65 let mut ts = TokenStream::new();
66 ts.add_must_use();
74 ts.add_getter("name", TokenStream::create_ref_ident_static("str"));
75 ts.wrap_braces({
76 let mut ts = TokenStream::new();
77 ts.add_ident("match");
78 ts.add_ident("self");
79 ts.wrap_braces({
80 let mut ts = TokenStream::new();
81 ts.extend(match_elements);
82 ts
83 });
84 ts
85 });
86
87 ts.add_ident("pub");
93 ts.add_ident("fn");
94 ts.add_ident("iter_names");
95 ts.extend(TokenStream::create_empty_type());
96 ts.add_single_arrow();
97 ts.add_ident("impl");
98 ts.add_ident("Iterator");
99 ts.wrap_generics({
100 let mut ts = TokenStream::new();
101 ts.add_ident("Item");
102 ts.add_punc('=');
103 ts.extend(TokenStream::create_ref_ident_static("str"));
104 ts
105 });
106 ts.wrap_braces({
107 let mut ts = TokenStream::new();
108 ts.add_ident("extern");
109 ts.add_ident("crate");
110 ts.add_ident("alloc");
111 ts.add_punc(';');
112 ts.add_ident("let");
113 ts.add_ident("names");
114 ts.add_punc('=');
115 ts.add_path(&["alloc", "vec"]);
116 ts.add_punc('!');
117 ts.wrap_brackets({
118 let mut ts = TokenStream::new();
119 ts.extend(name_literals);
120 ts
121 });
122 ts.add_punc(';');
123 ts.add_ident("names");
124 ts.add_punc('.');
125 ts.add_ident("into_iter");
126 ts.extend(TokenStream::create_empty_type());
127 ts
128 });
129
130 ts
131 });
132
133 out
134}
135
136#[proc_macro_derive(EnumIterItem)]
137pub fn enumitemiter_derive(input: TokenStream) -> TokenStream {
138 let input = parse_macro_input!(input as DeriveInput);
139 let Data::Enum(s) = input.data else {
140 return compile_error(&input, "Required enum type");
141 };
142 let enum_name = input.ident;
143
144 let mut items = alloc::vec::Vec::<TokenStream>::new();
145
146 for field in s.variants {
147 if !field.fields.is_empty() {
148 return compile_error(&field.span(), "Field cannot have fields");
149 }
150
151 let field_ident = field.ident;
152 items.push({
153 let mut ts = TokenStream::create_path(&["Self", &field_ident.to_string()]);
154 ts.add_punc(',');
155 ts
156 });
157 }
158 let mut out = TokenStream::new();
159 out.add_ident("impl");
160 out.add_path(&["irox_enums", "EnumIterItem"]);
161 out.add_ident("for");
162 out.add_ident(&enum_name.to_string());
163 out.wrap_braces({
164 let mut ts = TokenStream::new();
165 ts.add_ident("type");
166 ts.add_ident("Item");
167 ts.add_punc('=');
168 ts.add_ident(&enum_name.to_string());
169 ts.add_punc(';');
170
171 ts.add_fn("iter_items");
172 ts.extend([TokenStream::create_empty_type()]);
173 ts.add_single_arrow();
174 ts.add_path(&["irox_enums", "IntoIter"]);
175 ts.wrap_generics(TokenStream::create_path(&["Self", "Item"]));
176 ts.wrap_braces({
177 let mut ts = TokenStream::new();
178 ts.add_ident("extern");
179 ts.add_ident("crate");
180 ts.add_ident("alloc");
181 ts.add_punc(';');
182
183 ts.add_ident("let");
184 ts.add_ident("items");
185 ts.add_punc('=');
186 ts.add_path(&["alloc", "vec"]);
187 ts.add_punc('!');
188 ts.wrap_brackets({
189 let mut ts = TokenStream::new();
190 ts.extend(items);
191 ts
192 });
193 ts.add_punc(';');
194
195 ts.add_ident("items");
196 ts.add_punc('.');
197 ts.add_ident("into_iter");
198 ts.extend(TokenStream::create_empty_type());
199 ts
200 });
201 ts
202 });
203
204 out
205}
206
207#[proc_macro_derive(EnumTryFromStr)]
208pub fn tryfromstr_derive(input: TokenStream) -> TokenStream {
209 let input = parse_macro_input!(input as DeriveInput);
210 let Data::Enum(s) = input.data else {
211 return compile_error(&input, "Required enum type");
212 };
213 let enum_name = input.ident;
214 let mut out = TokenStream::new();
215 out.add_ident("impl");
216 out.add_ident("TryFrom");
217 out.wrap_generics(TokenStream::create_ref_ident("str"));
218 out.add_ident("for");
219 out.add_ident(&format!("{enum_name}"));
220 out.wrap_braces({
221 let mut ts = TokenStream::new();
222 ts.add_ident("type");
223 ts.add_ident("Error");
224 ts.add_punc('=');
225 ts.extend([TokenStream::create_empty_type()]);
226 ts.add_punc(';');
227
228 ts.add_fn("try_from");
229 ts.add_parens({
230 let mut ts1 = TokenStream::new();
231 ts1.add_ident("value");
232 ts1.add_punc(':');
233 ts1.extend([TokenStream::create_ref_ident("str")]);
234 ts1
235 });
236 ts.return_result(
237 TokenStream::create_ident("Self"),
238 TokenStream::create_path(&["Self", "Error"]),
239 );
240
241 ts.wrap_braces({
242 let mut ts = TokenStream::new();
243 ts.add_ident("Ok");
244 ts.add_parens({
245 let mut ts = TokenStream::new();
246 ts.add_ident("match");
247 ts.add_ident("value");
248 ts.wrap_braces({
249 let mut ts = TokenStream::new();
250 for field in s.variants {
251 if !field.fields.is_empty() {
252 return compile_error(&field.span(), "Field cannot have fields");
253 }
254
255 let field_ident = field.ident;
256 let field_name = field_ident.to_string();
257 ts.append_match_item(
258 TokenStream::create_literal(&field_name),
259 TokenStream::create_path(&["Self", &field_name]),
260 );
261 }
262 ts.add_ident("_");
263 ts.add_punc2('=', '>');
264 ts.wrap_braces({
265 let mut ts = TokenStream::new();
266 ts.add_ident("return");
267 ts.add_ident("Err");
268 ts.add_parens(TokenStream::create_empty_type());
269 ts
270 });
271 ts
272 });
273 ts
274 });
275 ts
276 });
277 ts
278 });
279 out
280}