advent_of_utils_macros/
lib.rs

1use quote::ToTokens;
2use syn::punctuated::Punctuated;
3
4#[proc_macro]
5pub fn add_days(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
6    let exprs = syn::parse_macro_input!(input with Punctuated::<syn::Expr, syn::Token![,]>::parse_terminated);
7    let numbers = evaluate_expr(exprs);
8
9    let mut modules: Vec<proc_macro2::TokenStream> = Vec::new();
10    let mut uses: Vec<proc_macro2::TokenStream> = Vec::new();
11    let mut map: Vec<proc_macro2::TokenStream> = Vec::new();
12    for i in numbers {
13        let day: syn::Ident = quote::format_ident!("day{:02}", i);
14        let method: syn::Ident = quote::format_ident!("Day{:02}", i);
15        modules.push(quote::quote! {
16            mod #day;
17        });
18        uses.push(quote::quote! {
19            use #day::#method;
20        });
21        map.push(quote::quote! {
22            solutions.insert(#i, Box::new(#method));
23        })
24    }
25
26    let expanded = quote::quote! {
27        #(#modules)*
28
29        #(#uses)*
30
31        fn internal_create_solutions() -> std::collections::HashMap<u8, Box<dyn advent_of_utils::Solution>> {
32            let mut solutions: std::collections::HashMap<u8, Box<dyn advent_of_utils::Solution>> = std::collections::HashMap::new();
33            #(#map)*
34
35            solutions
36        }
37
38        #[repr(C)]
39        pub struct SolutionsContainer {
40            solutions: *mut std::collections::HashMap<u8, Box<dyn advent_of_utils::Solution>>,
41        }
42
43        #[no_mangle]
44        pub extern "C" fn create_solutions() -> *mut SolutionsContainer {
45            let solutions = Box::new(internal_create_solutions());
46            let container = Box::new(SolutionsContainer {
47                solutions: Box::into_raw(solutions),
48            });
49
50            Box::into_raw(container)
51        }
52
53        #[no_mangle]
54        pub unsafe extern "C" fn destroy_solutions(container: *mut SolutionsContainer) {
55            if !container.is_null() {
56                let container = Box::from_raw(container);
57                if !container.solutions.is_null() {
58                    let _ = Box::from_raw(container.solutions);
59                }
60            }
61        }
62
63    };
64
65    proc_macro::TokenStream::from(expanded)
66}
67
68fn evaluate_expr(input: Punctuated<syn::Expr, syn::token::Comma>) -> Vec<u8> {
69    let mut numbers = Vec::new();
70    for expr in input {
71        match expr {
72            syn::Expr::Range(syn::ExprRange {
73                start,
74                limits: _,
75                end,
76                ..
77            }) => {
78                if let (Some(start), Some(end)) = (start, end) {
79                    // Extract start and end values
80                    if let (syn::Expr::Lit(start), syn::Expr::Lit(end)) = (*start, *end) {
81                        let start_num = start.to_token_stream().to_string().parse::<u8>().unwrap();
82                        let end_num = end.to_token_stream().to_string().parse::<u8>().unwrap();
83                        numbers.extend(start_num..=end_num);
84                    }
85                }
86            }
87            syn::Expr::Lit(lit) => {
88                let num = lit.to_token_stream().to_string().parse::<u8>().unwrap();
89                numbers.push(num);
90            }
91            _ => {}
92        }
93    }
94    numbers
95}