irox_enums_derive/
lib.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2023 IROX Contributors
3
4#![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        // println!("{}: {:?}", field.ident, field);
31        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        //#[must_use]
67        //fn name(&self) -> &'static str {
68        //    match self {
69        //        Self::#field_ident => #field_literal,
70        //        Self::#field_ident(..) => #field_literal,
71        //    }
72        //}
73        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        //pub fn iter_names() -> impl Iterator<Item=&'static str>{
88        //    extern crate alloc;
89        //    let names = alloc::vec![#(#names),*];
90        //    names.into_iter()
91        //}
92        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}