use sanitise_file_name; use std::fs::File; use std::io; use std::io::BufRead; use std::path; use std::path::Path; pub mod cafeteria; pub mod gift_shop; pub mod laboratories; pub mod lobby; pub mod movie_theater; pub mod playground; pub mod printing_department; pub mod secret_entrance; pub mod trash_compactor; pub trait Solver { const PUZZLE_INDEX: u8; const PUZZLE_NAME: &'static str; fn puzzle_index(&self) -> u8 { Self::PUZZLE_INDEX } fn puzzle_name(&self) -> &'static str { Self::PUZZLE_NAME } fn process_data(&self, reader: R) -> (u64, u64); } // The "../../data" and "../../../data" paths are useful for running the binary from // "target/release/" directory with "data/" directory in package root or parent directory. pub const DATA_PATHS: &'static [&'static str] = &["./data", "../data", "../../data", "../../../data"]; pub fn run(solver: S) { println!( "--- Day {}: {} ---", solver.puzzle_index(), solver.puzzle_name() ); match run_solver(solver, &DATA_PATHS) { Ok(result) => print_result(result), Err(error) => eprintln!("{}", error), } } pub fn run_solver(solver: S, paths: &[&str]) -> Result<(u64, u64), String> { let reader = read_data_file(input_filename(solver.puzzle_name()), paths)?; Ok(solver.process_data(reader)) } fn input_filename(puzzle_name: &str) -> String { sanitise_file_name::sanitise(puzzle_name) .to_lowercase() .replace(" ", "_") + ".txt" } fn print_result(result: (u64, u64)) { println!("Part 1: {:?}\nPart 2: {:?}\n", result.0, result.1); } fn read_data_file(filename: String, search_paths: &[T]) -> Result, String> where T: AsRef, { for path in search_paths { if let Ok(reader) = read_file(path.as_ref().join(&filename)) { return Ok(reader); } } let message = search_paths .iter() .filter_map(|p| { path::absolute(p.as_ref().join(&filename)) .ok() .and_then(|p| Some(p.display().to_string())) }) .collect::>() .join("\n"); Err(format!( "Cannot open puzzle input file, searched these paths:\n{}", message )) } // This code is taken from https://doc.rust-lang.org/stable/rust-by-example/std_misc/file/read_lines.html fn read_file(filename: T) -> io::Result> where T: AsRef, { let file = File::open(filename)?; Ok(io::BufReader::new(file)) }