Skip to main content

aoc_companion_codegen/
lib.rs

1use proc_macro::TokenStream;
2use std::collections::HashMap;
3use syn::{
4    parse::{Parse, ParseStream},
5    Ident, LitInt, Token,
6};
7
8struct Args {
9    day: LitInt,
10    parser: Option<Ident>,
11    part1: Option<Ident>,
12    part2: Option<Ident>,
13}
14
15impl Parse for Args {
16    fn parse(input: ParseStream) -> syn::Result<Self> {
17        let day = input.parse()?;
18        let mut args = HashMap::new();
19
20        input.parse::<Token![,]>()?;
21        while let Ok(id) = input.parse::<Ident>() {
22            match id.to_string().as_str() {
23                "parser" => {
24                    input.parse::<Token![=]>()?;
25                    let parser: Ident = input.parse()?;
26                    args.insert("parser", parser);
27                }
28                "part1" => {
29                    input.parse::<Token![=]>()?;
30                    let part1: Ident = input.parse()?;
31                    args.insert("part1", part1);
32                }
33
34                "part2" => {
35                    input.parse::<Token![=]>()?;
36                    let part2: Ident = input.parse()?;
37                    args.insert("part2", part2);
38                }
39                _ => return Err(syn::Error::new(id.span(), "unknown argument")),
40            }
41
42            if input.peek(Token![,]) {
43                input.parse::<Token![,]>()?;
44            } else {
45                break;
46            }
47        }
48
49        Ok(Args {
50            day,
51            parser: args.get("parser").cloned(),
52            part1: args.get("part1").cloned(),
53            part2: args.get("part2").cloned(),
54        })
55    }
56}
57
58/// Generates a main function for the day.
59/// # Panics
60/// Panics if the day is not a number.
61#[proc_macro_attribute]
62pub fn day(attr: TokenStream, _item: TokenStream) -> TokenStream {
63    let args = syn::parse_macro_input!(attr as Args);
64    let day: u8 = args.day.base10_parse().unwrap();
65    let parser = args.parser;
66    let part1 = args.part1;
67    let part2 = args.part2;
68
69    quote::quote! {
70        fn main() {
71            println!("Running Advent of Code Day {}", #day);
72            _ = aoc_companion::input::apply(&format!("input-day-{:02}.txt", #day), |input| {
73                aoc_companion::timed(|| {
74                    let input = #parser(&input);
75                    aoc_companion::timed(|| println!("Part 1: {}", #part1(&input)));
76                    aoc_companion::timed(|| println!("Part 2: {}", #part2(&input)));
77                });
78            });
79        }
80    }
81    .into()
82}