1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4 parse::{Parse, ParseStream},
5 Token,
6};
7
8struct Args {
9 year: syn::LitInt,
10 day: syn::LitInt,
11 part: syn::LitInt,
12}
13
14impl Parse for Args {
15 fn parse(input: ParseStream) -> syn::parse::Result<Self> {
16 let year = input.parse()?;
17 input.parse::<Token![,]>()?;
18 let day = input.parse()?;
19 input.parse::<Token![,]>()?;
20 let part: syn::LitInt = input.parse()?;
21
22 let part_val = part.to_string();
23 if part_val != "1" && part_val != "2" {
24 return Err(syn::Error::new(part.span(), "part must be 1 or 2"));
25 }
26
27 input.parse::<Token![,]>().ok();
29 Ok(Args { year, day, part })
30 }
31}
32
33fn as_challege_struct(args: Args, func: syn::ItemFn) -> syn::ExprStruct {
34 let year = args.year;
35 let day = args.day;
36 let part = args.part;
37 let funcname = &func.sig.ident;
38
39 syn::parse_quote! {
40 ::bddap_aoc::Challenge {
41 name: stringify!(#funcname),
42 year: #year,
43 day: #day,
44 part: #part,
45 run: {
46 #func
47 fn run(input: &str) -> String {
48 ::std::string::ToString::to_string(& #funcname ( input ))
49 }
50 run
51 },
52 }
53 }
54}
55
56#[proc_macro_attribute]
69pub fn register(attr: TokenStream, item: TokenStream) -> TokenStream {
70 let args = syn::parse_macro_input!(attr as Args);
71 let fn_def = syn::parse_macro_input!(item as syn::ItemFn);
72 let challenge = as_challenge(args, fn_def);
73 quote! {
74 #[::bddap_aoc::linkme::distributed_slice(::bddap_aoc::CHALLENGES)]
75 #challenge
76 }
77 .into()
78}
79
80#[proc_macro_attribute]
89pub fn unregistered_challenge(attr: TokenStream, item: TokenStream) -> TokenStream {
90 let args = syn::parse_macro_input!(attr as Args);
91 let fn_def = syn::parse_macro_input!(item as syn::ItemFn);
92 let declaration = as_challenge(args, fn_def);
93 quote!(#declaration).into()
94}
95
96fn as_challenge(args: Args, fn_def: syn::ItemFn) -> syn::ItemStatic {
97 let fn_name = fn_def.sig.ident.clone();
98 let visibility = fn_def.vis.clone();
99 let struct_literal = as_challege_struct(args, fn_def);
100 syn::parse_quote! {
101 #[allow(non_upper_case_globals)]
102 #visibility static #fn_name: ::bddap_aoc::Challenge = #struct_literal;
103 }
104}