common.rs 7.3 KB

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