1pub mod input;
2pub mod parse;
3pub mod utils;
4
5pub 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 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}