use clap::Parser; use std::fs::File; use std::io; use std::io::{BufRead, BufReader, Read}; use std::time::Instant; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Args { #[arg(short, long, default_value = "-")] file: String, #[arg(short, long, default_value_t = 1)] part: u8, } pub trait Day { fn example_input(&self) -> &'static str; fn example_result_part_1(&self) -> &'static str; fn example_result_part_2(&self) -> &'static str; fn read_lines(&self, input: BufReader>) -> Vec { let mut lines = Vec::new(); for line in input.lines() { match line { Ok(content) => lines.push(content), Err(e) => eprintln!("Error reading line: {}", e), } } lines } fn part_1(&self, input: BufReader>) -> String; fn part_2(&self, input: BufReader>) -> String; } pub fn main(day: &dyn Day) { let args = Args::parse(); let reader: Box = if args.file == "-" { Box::new(io::stdin()) } else { Box::new(File::open(&args.file).expect("Failed to open file")) }; let buffer = BufReader::new(reader); let result: String; let now = Instant::now(); if args.part == 1 { result = day.part_1(buffer); } else { result = day.part_2(buffer); } println!("Result: {result}"); println!("Time to run: {}s", now.elapsed().as_secs_f64()); } pub fn test_day(day: &T) { use std::io::Cursor; use std::io::{BufReader, Read}; let example_input = day.example_input(); // Test Part 1 let input = BufReader::new(Box::new(Cursor::new(example_input)) as Box); let output = day.part_1(input); assert_eq!(output.trim(), day.example_result_part_1(), "Part 1 failed"); // Test Part 2 let input = BufReader::new(Box::new(Cursor::new(example_input)) as Box); let output = day.part_2(input); assert_eq!(output.trim(), day.example_result_part_2(), "Part 2 failed"); }