1use proc_macro::TokenStream;
2use quote::quote;
3use quote::ToTokens;
4use quote::format_ident;
5use syn::{Lit, Expr, Token, Result};
6use syn::punctuated::Punctuated;
7use syn::parse::Parser;
8use syn::parse::Parse;
9use syn::parse::ParseStream;
10
11static mut DEMOS: u32 = 0;
12
13#[proc_macro]
14pub fn day(attrs: TokenStream) -> TokenStream {
15 let parser = Punctuated::<Expr, Token![,]>::parse_terminated;
16 let args = parser.parse(attrs).unwrap().iter()
17 .filter_map(|x| match x {
18 Expr::Lit(el) => Some(el.lit.clone()),
19 _ => None
20 })
21 .filter_map(|lit| match lit {
22 Lit::Int(li) => Some(li.base10_parse::<u32>().unwrap()),
23 _ => None,
24 }).collect::<Vec<u32>>();
25
26 let year = args.iter().max().unwrap();
27 let day = args.iter().min().unwrap();
28
29 let main: syn::ItemFn = syn::parse(quote! {
30 fn main() -> Result<(), ()> {
31 let fetch_start = std::time::Instant::now();
32 let input = adventage::fetch_day(#year, #day);
33 let fetch_runtime = adventage::format_runtime(fetch_start.elapsed());
34 println!("Fetching the input took {fetch_runtime}");
35
36 let parsed_start = std::time::Instant::now();
37 let parsed = parse(&input);
38 let parse_runtime = adventage::format_runtime(parsed_start.elapsed());
39 println!("Parsing the input took {parse_runtime}");
40
41 let part1_start = std::time::Instant::now();
42 let answer1 = part1(&parsed);
43 let part1_runtime = adventage::format_runtime(part1_start.elapsed());
44 println!("Answer 1: {answer1}, took {part1_runtime}");
45
46 let part2_start = std::time::Instant::now();
47 let answer2 = part2(&parsed);
48 let part2_runtime = adventage::format_runtime(part2_start.elapsed());
49 println!("Answer 2: {answer2}, took {part2_runtime}");
50 Ok(())
51 }
52 }.into()).unwrap();
53
54 main.to_token_stream().into()
55}
56
57#[derive(Debug)]
58enum Answer {
59 StringAnswer(String),
60 NumberAnswer(String),
61}
62
63impl Parse for Answer {
64 fn parse(input: ParseStream) -> Result<Self> {
65 let literal: Lit = input.parse()?;
66
67 match literal {
68 Lit::Str(ls) => Ok(Answer::StringAnswer(ls.value())),
69 Lit::Int(li) => Ok(Answer::NumberAnswer(li.base10_digits().to_string())),
70 _ => panic!(),
71 }
72 }
73}
74
75#[proc_macro]
76pub fn part1demo(attrs: TokenStream) -> TokenStream {
77 let parser = Punctuated::<Answer, Token![,]>::parse_terminated;
78 let result = parser.parse(attrs).unwrap();
79 let Answer::StringAnswer(input) = &result[0] else { panic!() };
80 let num = unsafe { DEMOS += 1; DEMOS };
81 let name = format_ident!("test_part1_{}", num);
82
83 let assertion = match &result[1] {
84 Answer::StringAnswer(answer) => quote! { assert_eq!(answer, #answer); },
85 Answer::NumberAnswer(answer) => quote! { assert_eq!(answer, #answer.parse().unwrap()); }
86 };
87
88 let test: syn::ItemFn = syn::parse(quote! {
89 #[test]
90 fn #name() {
91 let parsed_input = parse(#input);
92 let answer = part1(&parsed_input);
93
94 println!("Part 1 example:");
95 println!("{}", #input);
96 println!("Part 1 answer: {}", answer);
97 println!("--");
98
99 #assertion
100 }
101 }.into()).unwrap();
102
103 test.to_token_stream().into()
104}
105
106#[proc_macro]
107pub fn part2demo(attrs: TokenStream) -> TokenStream {
108 let parser = Punctuated::<Answer, Token![,]>::parse_terminated;
109 let result = parser.parse(attrs).unwrap();
110 let Answer::StringAnswer(input) = &result[0] else { panic!() };
111
112 let assertion = match &result[1] {
113 Answer::StringAnswer(answer) => quote! { assert_eq!(answer, #answer); },
114 Answer::NumberAnswer(answer) => quote! { assert_eq!(answer, #answer.parse().unwrap()); }
115 };
116
117 let num = unsafe { DEMOS += 1; DEMOS };
118 let name = format_ident!("test_part2_{}", num);
119
120 let test: syn::ItemFn = syn::parse(quote! {
121 #[test]
122 fn #name() {
123 let parsed_input = parse(#input);
124 let answer = part2(&parsed_input);
125
126 println!("Part 2 example:");
127 println!("{}", #input);
128 println!("Part 2 answer: {}", answer);
129 println!("--");
130
131 #assertion
132 }
133 }.into()).unwrap();
134
135 test.to_token_stream().into()
136}