Skip to content

Commit

Permalink
feat: command for creating files for a new day's puzzles
Browse files Browse the repository at this point in the history
  • Loading branch information
jhrcook committed Dec 15, 2023
1 parent b68f390 commit 2a7951d
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 74 deletions.
104 changes: 76 additions & 28 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,85 @@
mod data;
pub mod solutions;
use thiserror::Error;

pub fn run_day(data_dir: &str, day: &usize) {
#[derive(Error, Debug)]
pub enum Error {
#[error("Day not yet implemented: {}.", .0)]
DayNotImplemented(u32),
}

pub fn run_day(data_dir: &str, day: &u32) -> Result<(), Error> {
match day {
1 => solutions::day01::main(data_dir),
2 => solutions::day02::main(data_dir),
3 => solutions::day03::main(data_dir),
4 => solutions::day04::main(data_dir),
5 => solutions::day05::main(data_dir),
6 => solutions::day06::main(data_dir),
7 => solutions::day07::main(data_dir),
8 => solutions::day08::main(data_dir),
9 => solutions::day09::main(data_dir),
10 => solutions::day10::main(data_dir),
11 => solutions::day11::main(data_dir),
12 => solutions::day12::main(data_dir),
13 => solutions::day13::main(data_dir),
14 => solutions::day14::main(data_dir),
15 => solutions::day15::main(data_dir),
// 16 => solutions::day16::main(data_dir),
// 17 => solutions::day17::main(data_dir),
// 18 => solutions::day18::main(data_dir),
// 19 => solutions::day19::main(data_dir),
// 20 => solutions::day20::main(data_dir),
// 21 => solutions::day21::main(data_dir),
// 22 => solutions::day22::main(data_dir),
// 23 => solutions::day23::main(data_dir),
// 24 => solutions::day24::main(data_dir),
// 25 => solutions::day25::main(data_dir),
_ => panic!("Puzzle for day {} not completed yet.", day),
1 => {
solutions::day01::main(data_dir);
Ok(())
}
2 => {
solutions::day02::main(data_dir);
Ok(())
}
3 => {
solutions::day03::main(data_dir);
Ok(())
}
4 => {
solutions::day04::main(data_dir);
Ok(())
}
5 => {
solutions::day05::main(data_dir);
Ok(())
}
6 => {
solutions::day06::main(data_dir);
Ok(())
}
7 => {
solutions::day07::main(data_dir);
Ok(())
}
8 => {
solutions::day08::main(data_dir);
Ok(())
}
9 => {
solutions::day09::main(data_dir);
Ok(())
}
10 => {
solutions::day10::main(data_dir);
Ok(())
}
11 => {
solutions::day11::main(data_dir);
Ok(())
}
12 => {
solutions::day12::main(data_dir);
Ok(())
}
13 => {
solutions::day13::main(data_dir);
Ok(())
}
14 => {
solutions::day14::main(data_dir);
Ok(())
}
15 => {
solutions::day15::main(data_dir);
Ok(())
}
// <-- INSERT NEW DAY HERE -->
_ => Err(Error::DayNotImplemented(*day)),
}
}

pub fn run_all(data_dir: &str) {
(1..15).for_each(|day| run_day(data_dir, &day))
for day in 1..26 {
match run_day(data_dir, &day) {
Ok(()) => continue,
Err(Error::DayNotImplemented(_)) => break,
}
}
}
100 changes: 64 additions & 36 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,73 @@
mod new_day;

use aoc_2023::{run_all, run_day};
use clap::Parser;
use clap::{Parser, Subcommand};
use std::time::Instant;

/// Simple program to greet a person
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Data directory.
#[arg(default_value_t = String::from("puzzle-input"), help="Directory with input data files.")]
data_dir: String,
#[arg(
short,
long,
help = "Specific day to execute (runs all if not specified, default)."
)]
day: Option<usize>,
#[arg(
short,
long,
help = "Do not run any puzzles (to estimate start-up time)."
)]
empty: bool,
#[derive(Debug, Parser)]
#[command(author, version, about="Advent of Code 2023 command line interface.", long_about = None)]
struct Cli {
#[command(subcommand)]
command: Command,
}

#[derive(Debug, Subcommand)]
enum Command {
#[command()]
Run {
#[arg(default_value_t = String::from("puzzle-input"), help="Directory with input data files.")]
data_dir: String,
#[arg(
short,
long,
help = "Specific day to execute (runs all if not specified, default)."
)]
day: Option<u32>,
#[arg(
short,
long,
help = "Do not run any puzzles (to estimate start-up time)."
)]
empty: bool,
},
#[command()]
New {
#[arg()]
title: String,
},
}

fn main() {
let args = Args::parse();
let start = Instant::now();
if args.empty {
println!("Empty run.");
return;
}
match args.day {
Some(d) => {
println!("Running puzzle {}.", d);
run_day(&args.data_dir, &d);
let args = Cli::parse();
match args.command {
Command::New { title } => {
match new_day::make_new_day_files(&title) {
Ok(()) => println!("New day templates created. Good luck!"),
Err(e) => panic!("Failed to create new file templates:\n {:?}.", e),
};
}
None => {
println!("Running all puzzles.");
run_all(&args.data_dir);
Command::Run {
data_dir,
day,
empty,
} => {
let start = Instant::now();
if empty {
println!("Empty run.");
return;
}
match day {
Some(d) => {
println!("Running puzzle {}.", d);
run_day(&data_dir, &d).unwrap();
}
None => {
println!("Running all puzzles.");
run_all(&data_dir);
}
};
let duration = start.elapsed();
println!("Done! 🎉 -- Elapsed time: {:?}", duration);
}
};
let duration = start.elapsed();
println!("Done! 🎉 -- Elapsed time: {:?}", duration);
}
}
97 changes: 97 additions & 0 deletions src/new_day.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use std::fs;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
#[error("File aready exists: {}.", .0)]
FileExists(String),
#[error("File writing error.")]
Write(#[from] std::io::Error),
#[error("Parse integer error.")]
ParseInt(#[from] std::num::ParseIntError),
}

fn find_latest_day() -> Result<usize, Error> {
Ok(*fs::read_dir("src/solutions/")
.unwrap()
.map(|path| path.unwrap().file_name().into_string().unwrap())
.filter(|fname| fname.starts_with("day") & fname.ends_with("rs"))
.map(|fname| {
fname
.replace("day", "")
.replace(".rs", "")
.parse::<usize>()
.map_err(Error::ParseInt)
})
.collect::<Result<Vec<_>, Error>>()?
.iter()
.max()
.unwrap())
}

fn write_text(filename: &str, text: &str) -> Result<(), Error> {
let path = Path::new(filename);
if path.exists() {
log::error!("Trying to write to file that already exists.");
return Err(Error::FileExists(filename.to_string()));
}
let mut output: File = File::create(path)?;
write!(output, "{}", text).or_else(|e| Err(Error::Write(e)))
}

fn write_solution_file(day: &usize, title: &str) -> Result<(), Error> {
let text = fs::read_to_string("templates/_template_day.rs")
.unwrap()
.replace("TITLE", title)
.replace("00", format!("{}", day).as_str());

let file_name = format!("src/solutions/day{:02}.rs", day);
write_text(&file_name, &text)
}

fn write_test_file(day: &usize) -> Result<(), Error> {
let text = fs::read_to_string("templates/_template_test.rs")
.unwrap()
.replace("00", format!("{}", day).as_str());

let file_name = format!("tests/test_day{:02}.rs", day);
write_text(&file_name, &text)
}

fn add_day_to_mod_list(day: &usize) -> Result<(), Error> {
let fname = "src/solutions/mod.rs";
let mut file = OpenOptions::new()
.write(true)
.append(true)
.open(fname)
.unwrap();

let text = format!("pub mod day{:02};", day);
writeln!(file, "{}", text).or_else(|e| Err(Error::Write(e)))
}

fn add_day_to_lib_map(day: &usize) -> Result<(), Error> {
let repl_text = "// <-- INSERT NEW DAY HERE -->";
let new_text = format!(
"{:02} => Ok(solutions::day{:02}::main(data_dir)),\n{repl_text}",
day, day
);
let fname = "src/lib.rs";
let text = fs::read_to_string(fname)
.unwrap()
.replace(repl_text, &new_text);
let mut output: File = File::create(fname)?;
write!(output, "{}", text).or_else(|e| Err(Error::Write(e)))
}

pub fn make_new_day_files(title: &str) -> Result<(), Error> {
let next_day = find_latest_day()? + 1;
write_solution_file(&next_day, title)?;
write_test_file(&next_day)?;
add_day_to_mod_list(&next_day)?;
add_day_to_lib_map(&next_day)
}
10 changes: 0 additions & 10 deletions src/solutions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,3 @@ pub mod day12;
pub mod day13;
pub mod day14;
pub mod day15;
// pub mod day16;
// pub mod day17;
// pub mod day18;
// pub mod day19;
// pub mod day20;
// pub mod day21;
// pub mod day22;
// pub mod day23;
// pub mod day24;
// pub mod day25;
33 changes: 33 additions & 0 deletions templates/_template_day.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use crate::data::load;
use thiserror::Error;

#[derive(Error, Debug, PartialEq, Eq)]
pub enum PuzzleErr {
#[error("Input parsing error.")]
ParseInputError,
}

pub fn puzzle_1(input: &str) -> Result<usize, PuzzleErr> {
Ok(0)
}

pub fn main(data_dir: &str) {
println!("Day 00: PUZZLE_TITLE");
let data = load(data_dir, 00, None);

// Puzzle 1.
let answer_1 = puzzle_1(&data);
match answer_1 {
Ok(x) => println!(" Puzzle 1: {}", x),
Err(e) => panic!("No solution to puzzle 1: {}.", e),
}
// assert_eq!(answer_1, Ok(37113));

// Puzzle 2.
// let answer_2 = puzzle_2(&data);
// match answer_2 {
// Ok(x) => println!(" Puzzle 2: {}", x),
// Err(e) => panic!("No solution to puzzle 2: {}", e),
// }
// assert_eq!(answer_2, Ok(30449))
}
11 changes: 11 additions & 0 deletions templates/_template_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use aoc_2023::solutions::day00::puzzle_1;

const EXAMPLE_INPUT_1: &str = "
";

#[test]
fn puzzle_1_example_1() {
let _ = env_logger::try_init();
assert_eq!(puzzle_1(self::EXAMPLE_INPUT_1), -1);
}

0 comments on commit 2a7951d

Please sign in to comment.