1mod input;
28mod parse;
29
30use std::{env, fs, path::Path, time::Instant};
31
32pub use input::{Input, Lines};
33pub use parse::{Ints, IterUnwrap, Parse, Parser, UInts};
34
35type Part<T> = fn(Input) -> T;
36
37#[macro_export]
46macro_rules! parts {
47 () => {
48 fn main() {
49 aoc::run::<u8, u8>(None, None);
50 }
51 };
52 (1) => {
53 fn main() {
54 aoc::run::<_, u8>(Some(part_1), None);
55 }
56 };
57 (2) => {
58 fn main() {
59 aoc::run::<u8, _>(None, Some(part_2));
60 }
61 };
62 (1, 2) => {
63 fn main() {
64 aoc::run(Some(part_1), Some(part_2));
65 }
66 };
67}
68
69pub fn run<T1, T2>(part_1: Option<Part<T1>>, part_2: Option<Part<T2>>)
75where
76 T1: ToString,
77 T2: ToString,
78{
79 let args: Vec<_> = env::args().collect();
80 if args.len() != 3 {
81 panic!("incorrect number of arguments");
82 }
83 let data = args[1].as_str();
84 let mut data_path = Path::new("data").join(data);
85 if !data_path.is_dir() {
86 panic!("no data directory");
87 }
88 let part = args[2].as_str();
89 if part != "1" && part != "2" {
90 panic!("invalid part argument");
91 }
92 let out_path = data_path.join(part).join("out");
93 if out_path.try_exists().unwrap() {
94 if !out_path.is_dir() {
95 panic!("unexpected out file found");
96 }
97 } else {
98 fs::create_dir_all(&out_path).unwrap();
99 }
100 data_path.push("input");
101 let unimplemented_path = out_path.join("unimplemented");
102 if part == "1" {
103 let Some(part_1) = part_1 else {
104 fs::write(unimplemented_path, "").unwrap();
105 return;
106 };
107 implemented(&unimplemented_path);
108 run_part(&data_path, &out_path, part_1);
109 } else {
110 let Some(part_2) = part_2 else {
111 fs::write(unimplemented_path, "").unwrap();
112 return;
113 };
114 implemented(&unimplemented_path);
115 run_part(&data_path, &out_path, part_2);
116 }
117}
118
119fn run_part<T>(data_path: &Path, out_path: &Path, part_n: Part<T>)
120where
121 T: ToString,
122{
123 if !data_path.is_file() {
124 panic!("no input file");
125 }
126 let input = fs::read_to_string(data_path).unwrap();
127 let input = input.trim_end();
128 if input.is_empty() {
129 panic!("input file is empty");
130 }
131 let lines: Vec<_> = input.lines().collect();
132 let input = Input::new(input, &lines);
133 let start = Instant::now();
134 let answer = part_n(input);
135 let time = start.elapsed().as_nanos();
136 let answer = answer.to_string();
137 fs::write(out_path.join("answer"), answer).unwrap();
138 fs::write(out_path.join("time"), time.to_string()).unwrap();
139}
140
141fn implemented(path: &Path) {
142 if path.try_exists().unwrap() {
143 if path.is_file() {
144 fs::remove_file(path).unwrap();
145 } else {
146 panic!("unexpected unimplemented directory found");
147 }
148 }
149}