123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- use std::{fs, str};
- use std::error::Error;
- use std::fs::File;
- use std::io::{self, BufRead, Write};
- use std::path::Path;
- pub type AOCResult = Result<[Option<String>; 2], Box<dyn Error>>;
- pub trait AOCProgram {
- fn run(&self, input: &Vec<String>) -> AOCResult;
- }
- impl<F: Fn(&Vec<String>) -> AOCResult> AOCProgram for F {
- fn run(&self, input: &Vec<String>) -> AOCResult {
- self(input)
- }
- }
- fn read_lines<P>(filename: P) -> io::Result<Vec<String>>
- where P: AsRef<Path> {
- let file = File::open(filename)?;
- let lines = io::BufReader::new(file).lines();
- return lines.collect();
- }
- fn pretty_print_iter(vec: &[String]) -> String {
- vec.iter().fold(String::new(), |acc, s| acc + &s + "\n")
- }
- pub fn run<F: AOCProgram>(day: &str, prog: &F) -> Result<(), Box<dyn Error>> {
- let arguments = std::env::args();
- let arguments = arguments::parse(arguments)?;
- let part: Option<usize> = arguments.get("part");
- let output_file: Option<String> = arguments.get("output-file");
- // let skip_test = arguments.get::<bool>("skip-test").unwrap_or(false);
- let input = read_lines(format!("./inputs/{}.txt", day))?;
- let result = prog.run(&input);
- let output: Result<String, Box<dyn Error>> = match result {
- Err(e) => {
- let res = format!("Failed on input\"\n{}\" with error\n{}", pretty_print_iter(&input), e).into();
- return Err(res);
- }
- Ok(result) =>
- if part.is_none() {
- match &result {
- [Some(o1), Some(o2)] => Ok(format!("1:{}\n2:{}", o1, o2)),
- [Some(o1), None] => Ok(format!("1:{}", o1)),
- [None, Some(o2)] => Ok(format!("2:{}", o2)),
- [None, None] => Ok(format!("No output"))
- }
- } else {
- let part = part.unwrap();
- let result = &result[part - 1];
- match result {
- None => {
- let res = format!("Part {} not implemented", part).into();
- return Err(res);
- }
- Some(s) => Ok(s.to_owned())
- }
- }
- };
- let output = output?;
- return match output_file {
- Some(output_file) => {
- let output = output.as_bytes();
- let mut file = File::create(output_file)?;
- file.write(output).map(|_| ())
- }
- None => Ok(println!("{}", output))
- }.map_err(|e| e.into());
- }
- pub fn run_prog<F: AOCProgram>(day: &str, prog: &F) {
- let inputs = files_to_vec(day, "./inputs");
- if let Err(e) = inputs {
- eprintln!("Error occurred while reading input files {}", e);
- return;
- }
- let inputs = inputs.unwrap();
-
- for (path, input) in inputs {
- let result = prog.run(&input);
- match result {
- Err(e) => println!("{}: Failed on input\"\n{}\" with error\n{}", path, pretty_print_iter(&input), e),
- Ok(result) => match &result {
- [Some(o1), Some(o2)] => println!("{}:\n\t1:{}\n\t2:{}", path, o1, o2),
- [Some(o1), None] => println!("{}:\n\t1:{}", path, o1),
- [None, Some(o2)] => println!("{}:\n\t2:{}", path, o2),
- [None, None] => println!("{}: No output", path)
- }
- }
- }
- }
- pub fn run_test<F: AOCProgram>(day: &str, prog: &F) -> bool {
- let expected_re: regex::Regex = regex::Regex::new(r"^([^\$]+)?(?:\$([^\$]+))?$").unwrap();
- let inputs = files_to_vec(day, "./test_inputs");
- if let Err(e) = inputs {
- eprintln!("Error occurred while reading test input files {}", e);
- return false;
- }
- let inputs = inputs.unwrap();
- if inputs.len() == 0 {
- println!("Tests failed: No valid input files given");
- return false;
- }
- let mut succeed = true;
- for (path, mut input) in inputs.clone() {
- let expected = input.pop();
- if expected.is_none() {
- println!("{}: empty", path);
- continue;
- }
- let expected = expected.unwrap();
- let exp_caps = expected_re.captures(&expected);
- let exp1 = exp_caps.as_ref().and_then(|cap| cap.get(1)).map(|m| m.as_str());
- let exp2 = exp_caps.as_ref().and_then(|cap| cap.get(2)).map(|m| m.as_str());
- let result = test::<F>(prog, &input, [exp1, exp2], &path);
- succeed = succeed && result[0].unwrap_or(true) && result[1].unwrap_or(true);
- }
- return succeed;
- }
- fn test<F: AOCProgram>(prog: &F, input: &Vec<String>, expected: [Option<&str>; 2], path: &str) -> [Option<bool>; 2] {
- let result = prog.run(input);
- if result.is_err() {
- println!("{}: Program failed on input\"\n{}\" with error\n{}",
- path, pretty_print_iter(&input[..10.min(input.len())]), result.unwrap_err());
- return [Some(false), Some(false)];
- }
- let result = &result.unwrap();
- let succeeded = [
- result[0]
- .as_ref()
- .ok_or("Program did not produce output")
- .and_then(|r| expected[0]
- .ok_or("No expected output")
- .map(|e| e == r.as_str())
- ),
- result[1]
- .as_ref()
- .ok_or("Program did not produce output")
- .and_then(|r| expected[1]
- .ok_or("No expected output")
- .map(|e| e == r.as_str())
- ),
- ];
- if succeeded[0].is_err() {
- println!("{}: Skipped testing result 1: {}", path, succeeded[0].unwrap_err())
- } else {
- if succeeded[0].unwrap() {
- println!("{}: Result 1 succeeded", path)
- } else {
- eprintln!("{}: Result 1 failed: Wrong output for input \"\n{}\".\n\tExpected \"{}\"\n\tReceived \"{}\"",
- path, pretty_print_iter(&input[..10.min(input.len())]), expected[0].as_ref().unwrap(), result[0].as_ref().unwrap());
- }
- }
- if succeeded[1].is_err() {
- println!("{}: Skipped testing result 2: {}", path, succeeded[1].unwrap_err())
- } else {
- if succeeded[1].unwrap() {
- println!("{}: Result 2 succeeded", path)
- } else {
- eprintln!("{}: Result 2 failed: Wrong output for input \"\n{}\".\n\tExpected {}\n\tReceived {}",
- path, pretty_print_iter(&input[..10.min(input.len())]), expected[1].as_ref().unwrap(), result[1].as_ref().unwrap());
- }
- }
- return [succeeded[0].ok(), succeeded[1].ok()];
- }
- fn files_to_vec<'a>(day: &str, input_path: &str) -> Result<Vec<(String, Vec<String>)>, Box<dyn Error>> {
- let dir = fs::read_dir(input_path)?;
- let file_name_re = regex::Regex::new(&format!(r"{}(?:-.*)?(?:\..*)", day))?;
- let results = dir.map::<io::Result<_>, _>(|f| {
- let f = f?;
- if !f.metadata()?.is_file() {
- return Ok(None);
- }
- let path = f.path();
- let file_name = path.file_name().and_then(|s| s.to_str());
- if !file_name.map(|s| file_name_re.is_match(s)).unwrap_or(false) {
- return Ok(None);
- }
- let file_name = String::from(file_name.unwrap());
- return Ok(Some((file_name, read_lines(f.path())?)));
- }).fold(vec![], |mut acc, r| {
- match r {
- Ok(None) => (),
- Ok(Some(v)) => acc.push(v),
- Err(e) => eprintln!("Error while processing file: {}", e),
- }
- return acc;
- });
- return Ok(results);
- }
|