aoc_main/
lib.rs

1pub mod input;
2pub mod parse;
3pub mod utils;
4
5// Reexport some crates for the generated main
6pub use clap;
7pub use colored;
8
9#[cfg(feature = "bench")]
10pub use criterion;
11
12use clap::{Arg, ArgAction, Command, ValueHint};
13
14pub fn args(year: u16) -> Command {
15    Command::new(format!("Advent of Code {year}"))
16        .about(format!(
17            "Main page of the event: https://adventofcode.com/{year}/"
18        ))
19        .arg(
20            Arg::new("stdin")
21                .short('i')
22                .long("stdin")
23                .action(ArgAction::SetTrue)
24                .conflicts_with("file")
25                .help("Read input from stdin instead of downloading it"),
26        )
27        .arg(
28            Arg::new("file")
29                .short('f')
30                .long("file")
31                .conflicts_with("stdin")
32                .value_hint(ValueHint::FilePath)
33                .help("Read input from file instead of downloading it"),
34        )
35        .arg(
36            Arg::new("days")
37                .short('d')
38                .long("day")
39                .value_name("day num")
40                .help("Days to execute. By default all implemented days will run"),
41        )
42        .arg(
43            Arg::new("bench")
44                .short('b')
45                .long("bench")
46                .action(ArgAction::SetTrue)
47                .help("Run criterion benchmarks"),
48        )
49        .arg(
50            Arg::new("all")
51                .short('a')
52                .long("all")
53                .action(ArgAction::SetTrue)
54                .conflicts_with("days")
55                .help("Run all days"),
56        )
57}
58
59#[macro_export]
60macro_rules! base_main {
61    ( year $year: expr; $( $tail: tt )* ) => {
62        use std::fs::read_to_string;
63        use std::io::Read;
64        use std::time::Instant;
65
66        use $crate::{bench_day, extract_day, parse, run_day};
67
68        const YEAR: u16 = $year;
69
70        fn main() {
71            let mut opt = $crate::args(YEAR).get_matches();
72
73            let days: Vec<_> = {
74                if let Some(opt_days) = opt.get_many::<String>("days") {
75                    let opt_days: Vec<&str> = opt_days.map(|s| s.as_str()).collect();
76                    let days = parse! { extract_day {}; $( $tail )* };
77
78                    let ignored_days: Vec<_> = opt_days
79                        .iter()
80                        .filter(|day| !days.contains(&format!("day{day}").as_str()))
81                        .copied()
82                        .collect();
83
84                    if !ignored_days.is_empty() {
85                        eprintln!(r"/!\ Ignoring unimplemented days: {}", ignored_days.join(", "));
86                    }
87
88                    opt_days
89                        .into_iter()
90                        .filter(|day| days.contains(&format!("day{}", day).as_str()))
91                        .collect()
92                } else if opt.get_flag("all") {
93                    parse!(extract_day {}; $( $tail )*)
94                        .iter()
95                        .map(|s| &s[3..])
96                        .collect()
97                } else {
98                    // Get most recent day, assuming the days are sorted
99                    vec![parse!(extract_day {}; $( $tail )*)
100                        .iter()
101                        .map(|s| &s[3..])
102                        .last()
103                        .expect("No day implemenations found")]
104                }
105            };
106
107            if opt.get_flag("bench") {
108                bench(days);
109            } else {
110                if days.len() > 1 && (opt.contains_id("stdin") || opt.contains_id("file")) {
111                    eprintln!(r"/!\ You are using a personalized output over several days which can");
112                    eprintln!(r"    be missleading. If you only intend to run solutions for a");
113                    eprintln!(r"    specific day, you can specify it by using the `-d DAY_NUM` flag.");
114                }
115
116                for (i, day) in days.iter().enumerate() {
117                    parse! {
118                        run_day { i, format!("day{}", day), YEAR, opt };
119                        $( $tail )*
120                    };
121                }
122            }
123        }
124    }
125}
126
127#[cfg(feature = "bench")]
128#[macro_export]
129macro_rules! main {
130    ( year $year: expr; $( $tail: tt )* ) => {
131        $crate::base_main! { year $year; $( $tail )* }
132
133        use $crate::criterion::Criterion;
134
135        fn bench(days: Vec<&str>) {
136            let mut criterion = Criterion::default().with_output_color(true);
137
138            for day in days.into_iter() {
139                parse! {
140                    bench_day { &mut criterion, format!("day{}", day), YEAR };
141                    $( $tail )*
142                };
143            }
144
145            criterion.final_summary();
146        }
147    }
148}
149
150#[cfg(not(feature = "bench"))]
151#[macro_export]
152macro_rules! main {
153    ( year $year: expr; $( $tail: tt )* ) => {
154        $crate::base_main! { year $year; $( $tail )* }
155
156        fn bench(days: Vec<&str>) {
157            println!("Benchmarks not available, please enable `bench` feature for cargo-main.");
158        }
159    }
160}