common.rs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. use std::{fs, str};
  2. use std::error::Error;
  3. use std::fs::File;
  4. use std::io::{self, BufRead, Write};
  5. use std::path::Path;
  6. pub type AOCResult = Result<[Option<String>; 2], Box<dyn Error>>;
  7. pub trait AOCProgram {
  8. fn run(&self, input: &Vec<String>) -> AOCResult;
  9. }
  10. impl<F: Fn(&Vec<String>) -> AOCResult> AOCProgram for F {
  11. fn run(&self, input: &Vec<String>) -> AOCResult {
  12. self(input)
  13. }
  14. }
  15. fn read_lines<P>(filename: P) -> io::Result<Vec<String>>
  16. where P: AsRef<Path> {
  17. let file = File::open(filename)?;
  18. let lines = io::BufReader::new(file).lines();
  19. return lines.collect();
  20. }
  21. pub fn run<F: AOCProgram>(day: &str, prog: &F) -> Result<(), Box<dyn Error>> {
  22. let arguments = std::env::args();
  23. let arguments = arguments::parse(arguments)?;
  24. let part: Option<usize> = arguments.get("part");
  25. let output_file: Option<String> = arguments.get("output-file");
  26. // let skip_test = arguments.get::<bool>("skip-test").unwrap_or(false);
  27. let input = read_lines(format!("./inputs/{}.txt", day))?;
  28. let result = prog.run(&input);
  29. let output: Result<String, Box<dyn Error>> = match result {
  30. Err(e) => {
  31. let res = format!("Failed on input {:?} with error {:?}", &input, e).into();
  32. return Err(res);
  33. }
  34. Ok(result) =>
  35. if part.is_none() {
  36. match &result {
  37. [Some(o1), Some(o2)] => Ok(format!("1:{}\n2:{}", o1, o2)),
  38. [Some(o1), None] => Ok(format!("1:{}", o1)),
  39. [None, Some(o2)] => Ok(format!("2:{}", o2)),
  40. [None, None] => Ok(format!("No output"))
  41. }
  42. } else {
  43. let part = part.unwrap();
  44. let result = &result[part - 1];
  45. match result {
  46. None => {
  47. let res = format!("Part {} not implemented", part).into();
  48. return Err(res);
  49. }
  50. Some(s) => Ok(s.to_owned())
  51. }
  52. }
  53. };
  54. let output = output?;
  55. return match output_file {
  56. Some(output_file) => {
  57. let output = output.as_bytes();
  58. let mut file = File::create(output_file)?;
  59. file.write(output).map(|_| ())
  60. }
  61. None => Ok(println!("{}", output))
  62. }.map_err(|e| e.into());
  63. }
  64. pub fn run_prog<F: AOCProgram>(day: &str, prog: &F) {
  65. let inputs = files_to_vec(day, "./inputs");
  66. if let Err(e) = inputs {
  67. eprintln!("Error occurred while reading input files {:?}", e);
  68. return;
  69. }
  70. let inputs = inputs.unwrap();
  71. for (path, input) in inputs {
  72. let result = prog.run(&input);
  73. match result {
  74. Err(e) => println!("{}: Failed on input {:?} with error {:?}", path, &input, e),
  75. Ok(result) => match &result {
  76. [Some(o1), Some(o2)] => println!("{}:\n\t1:{}\n\t2:{}", path, o1, o2),
  77. [Some(o1), None] => println!("{}:\n\t1:{}", path, o1),
  78. [None, Some(o2)] => println!("{}:\n\t2:{}", path, o2),
  79. [None, None] => println!("{}: No output", path)
  80. }
  81. }
  82. }
  83. }
  84. pub fn run_test<F: AOCProgram>(day: &str, prog: &F) -> bool {
  85. let expected_re: regex::Regex = regex::Regex::new(r"^([^\$]+)?(?:\$([^\$]+))?$").unwrap();
  86. let inputs = files_to_vec(day, "./test_inputs");
  87. if let Err(e) = inputs {
  88. eprintln!("Error occurred while reading test input files {:?}", e);
  89. return false;
  90. }
  91. let inputs = inputs.unwrap();
  92. if inputs.len() == 0 {
  93. println!("Tests failed: No valid input files given");
  94. return false;
  95. }
  96. let mut succeed = true;
  97. for (path, mut input) in inputs.clone() {
  98. let expected = input.pop();
  99. if expected.is_none() {
  100. println!("{}: empty", path);
  101. continue;
  102. }
  103. let expected = expected.unwrap();
  104. let exp_caps = expected_re.captures(&expected);
  105. let exp1 = exp_caps.as_ref().and_then(|cap| cap.get(1)).map(|m| m.as_str());
  106. let exp2 = exp_caps.as_ref().and_then(|cap| cap.get(2)).map(|m| m.as_str());
  107. let result = test::<F>(prog, &input, [exp1, exp2], &path);
  108. succeed = succeed && result[0].unwrap_or(true) && result[1].unwrap_or(true);
  109. }
  110. return succeed;
  111. }
  112. fn test<F: AOCProgram>(prog: &F, input: &Vec<String>, expected: [Option<&str>; 2], path: &str) -> [Option<bool>; 2] {
  113. let result = prog.run(input);
  114. if result.is_err() {
  115. println!("{}: Program failed on input {:?} with error {:?}",
  116. path, &input[..10.min(input.len())], result.unwrap_err());
  117. return [Some(false), Some(false)];
  118. }
  119. let result = &result.unwrap();
  120. let succeeded = [
  121. result[0]
  122. .as_ref()
  123. .ok_or("Program did not produce output")
  124. .and_then(|r| expected[0]
  125. .ok_or("No expected output")
  126. .map(|e| e == r.as_str())
  127. ),
  128. result[1]
  129. .as_ref()
  130. .ok_or("Program did not produce output")
  131. .and_then(|r| expected[1]
  132. .ok_or("No expected output")
  133. .map(|e| e == r.as_str())
  134. ),
  135. ];
  136. if succeeded[0].is_err() {
  137. println!("{}: Skipped testing result 1: {}", path, succeeded[0].unwrap_err())
  138. } else {
  139. if succeeded[0].unwrap() {
  140. println!("{}: Result 1 succeeded", path)
  141. } else {
  142. eprintln!("{}: Result 1 failed: Wrong output for input {:?}.\n\tExpected \"{}\"\n\tReceived \"{}\"",
  143. path, &input[..10.min(input.len())], expected[0].as_ref().unwrap(), result[0].as_ref().unwrap());
  144. }
  145. }
  146. if succeeded[1].is_err() {
  147. println!("{}: Skipped testing result 2: {}", path, succeeded[1].unwrap_err())
  148. } else {
  149. if succeeded[1].unwrap() {
  150. println!("{}: Result 2 succeeded", path)
  151. } else {
  152. eprintln!("{}: Result 2 failed: Wrong output for input {:?}.\n\tExpected {}\n\tReceived {}",
  153. path, &input[..10.min(input.len())], expected[1].as_ref().unwrap(), result[1].as_ref().unwrap());
  154. }
  155. }
  156. return [succeeded[0].ok(), succeeded[1].ok()];
  157. }
  158. fn files_to_vec<'a>(day: &str, input_path: &str) -> Result<Vec<(String, Vec<String>)>, Box<dyn Error>> {
  159. let dir = fs::read_dir(input_path)?;
  160. let file_name_re = regex::Regex::new(&format!(r"{}(?:-.*)?(?:\..*)", day))?;
  161. let results = dir.map::<io::Result<_>, _>(|f| {
  162. let f = f?;
  163. if !f.metadata()?.is_file() {
  164. return Ok(None);
  165. }
  166. let path = f.path();
  167. let file_name = path.file_name().and_then(|s| s.to_str());
  168. if !file_name.map(|s| file_name_re.is_match(s)).unwrap_or(false) {
  169. return Ok(None);
  170. }
  171. let file_name = String::from(file_name.unwrap());
  172. return Ok(Some((file_name, read_lines(f.path())?)));
  173. }).fold(vec![], |mut acc, r| {
  174. match r {
  175. Ok(None) => (),
  176. Ok(Some(v)) => acc.push(v),
  177. Err(e) => eprintln!("Error while processing file: {:?}", e),
  178. }
  179. return acc;
  180. });
  181. return Ok(results);
  182. }