Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-year support #77

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[alias]
today = "run --quiet --release --features today -- today"
today = "run --quiet --release -- today"
scaffold = "run --quiet --release -- scaffold"
download = "run --quiet --release -- download"
read = "run --quiet --release -- read"
switch-year = "run --quiet --release -- switch-year"

solve = "run --quiet --release -- solve"
all = "run --quiet --release -- all"
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@ data/inputs/*
!data/inputs/.keep
data/puzzles/*
!data/puzzles/.keep
years/*/inputs/*
!years/*/inputs/.keep
years/*/puzzles/*
!years/*/puzzles/.keep

# Dhat
dhat-heap.json

# Benchmarks

data/timings.json
years/*/timings.json
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ debug = 1

[features]
dhat-heap = ["dhat"]
today = ["chrono"]
test_lib = []

[dependencies]

# Template dependencies
chrono = { version = "0.4.38", optional = true }
chrono = "0.4.38"
dhat = { version = "0.3.3", optional = true }
pico-args = "0.5.0"
tinyjson = "2.5.1"
fs_extra = "1.3.0"

# Solution dependencies
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<img src="./.assets/christmas_ferris.png" width="164">

# 🎄 Advent of Code {year}
# 🎄 Advent of Code

Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.rust-lang.org/).

Expand Down Expand Up @@ -201,6 +201,28 @@ cargo today
# ...the input...
```

### ➡️ Change year

You can use the `switch-year` command to navigate through years.

The files of the year defined in the environment variable `AOC_YEAR` in `.cargo/config.toml` are as usual in `src/` and `data/` folders, while the others are stored in `years/{AOC_YEAR}/`. Benchmarks on the readme are automatically updated.

```sh
# example: `cargo switch-year 2015` with no files already written for 2015
cargo switch-year 2015

# output:
# No existing files for year 2015, generating blank folders.
# ---
# 🎄 Successfully switched to year 2015.
```

> [!TIP]
> Remember to switch to the last event year before doing any commits on your personal repo, or else you will have many files changes as there were moved during year switches.

> Please note that [stars tracking](#automatically-track-️-progress-in-the-readme) will still track the year you specified in the GitHub action and will not be changed.


### ➡️ Format code

```sh
Expand Down
27 changes: 18 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use advent_of_code::template::commands::{all, download, read, scaffold, solve, time};
use args::{parse, AppArguments};

#[cfg(feature = "today")]
use advent_of_code::template::commands::{all, download, read, scaffold, solve, switchyear, time};
use advent_of_code::template::Day;
#[cfg(feature = "today")]
use advent_of_code::template::{ANSI_BOLD, ANSI_RESET};
use args::{parse, AppArguments};
use std::process;

mod args {
use advent_of_code::template::Day;
use advent_of_code::template::{Day, Year};
use std::process;

pub enum AppArguments {
Expand Down Expand Up @@ -36,8 +34,10 @@ mod args {
day: Option<Day>,
store: bool,
},
#[cfg(feature = "today")]
Today,
SwitchYear {
year: Year,
},
}

pub fn parse() -> Result<AppArguments, Box<dyn std::error::Error>> {
Expand Down Expand Up @@ -74,8 +74,10 @@ mod args {
submit: args.opt_value_from_str("--submit")?,
dhat: args.contains("--dhat"),
},
#[cfg(feature = "today")]
Some("today") => AppArguments::Today,
Some("switch-year") => AppArguments::SwitchYear {
year: args.free_from_str()?,
},
Some(x) => {
eprintln!("Unknown command: {x}");
process::exit(1);
Expand All @@ -96,6 +98,10 @@ mod args {
}

fn main() {
println!(
"🎄{ANSI_BOLD} Advent of Code {} {ANSI_RESET}🎄",
std::env::var("AOC_YEAR").unwrap()
);
match parse() {
Err(err) => {
eprintln!("Error: {err}");
Expand All @@ -122,10 +128,10 @@ fn main() {
dhat,
submit,
} => solve::handle(day, release, dhat, submit),
#[cfg(feature = "today")]
AppArguments::Today => {
match Day::today() {
Some(day) => {
switchyear::handle_today();
scaffold::handle(day, false);
download::handle(day);
read::handle(day)
Expand All @@ -139,6 +145,9 @@ fn main() {
}
};
}
AppArguments::SwitchYear { year } => {
switchyear::handle(year);
}
},
};
}
1 change: 1 addition & 0 deletions src/template/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ pub mod download;
pub mod read;
pub mod scaffold;
pub mod solve;
pub mod switchyear;
pub mod time;
91 changes: 91 additions & 0 deletions src/template/commands/switchyear.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use crate::template::year::Year;
use std::{env, fs, path::PathBuf};

extern crate fs_extra;

pub fn handle(year: Year) {
let env_year = Year::__new_unchecked(env::var("AOC_YEAR").unwrap().parse().unwrap());
if year == env_year {
println!("🔔 You are already in the year you want to switch to.");
} else {
switch_to_year(year, env_year);
println!(
"---\n🎄 Successfully switched to year {}.",
year.into_inner()
);
}
}

pub fn handle_today() {
let year = Year::this_year().unwrap();
let env_year = Year::new(env::var("AOC_YEAR").unwrap().parse().unwrap()).unwrap();
if year != env_year {
switch_to_year(year, env_year);
println!(
"🎄 Automatically switched to this year: {}.",
year.into_inner()
);
}
}

fn create_folder_with_gitkeep(path: PathBuf) {
fs_extra::dir::create_all(&path, false).unwrap();
fs::write(path.join(".keep"), "").unwrap();
}

pub fn switch_to_year(year: Year, previous_year: Year) {
let cwd = env::current_dir().unwrap();

// Move src and data files to years/
let src = cwd.join("src");
let data = cwd.join("data");
let bin = src.join("bin");
let examples = data.join("examples");
let inputs = data.join("inputs");
let puzzles = data.join("puzzles");
let years = cwd.join("years");
let destination = years.join(previous_year.into_inner().to_string());

let default_copy = fs_extra::dir::CopyOptions::new();
let mut inside_copy = fs_extra::dir::CopyOptions::new();
inside_copy.content_only = true;
fs_extra::dir::create_all(&destination, false).unwrap();
fs_extra::dir::move_dir(&bin, &destination, &default_copy).unwrap();
fs_extra::dir::move_dir(&data, &destination, &inside_copy).unwrap();

// Move years/ to src and data files
let source = years.join(year.into_inner().to_string());
if source.exists() {
let source_bin = source.join("bin");
fs_extra::dir::move_dir(&source_bin, &src, &default_copy).unwrap();
fs_extra::dir::move_dir(&source, &data, &inside_copy).unwrap();
println!(
"Found existing files for year {}, moved them.",
year.into_inner()
);
} else {
println!(
"No existing files for year {}, generating blank folders.",
year.into_inner()
);
create_folder_with_gitkeep(bin);
create_folder_with_gitkeep(examples);
create_folder_with_gitkeep(inputs);
create_folder_with_gitkeep(puzzles);
}

// Set the environment variable
std::env::set_var("AOC_YEAR", year.into_inner().to_string());

// Write Cargo.toml
let config_toml = cwd.join(".cargo").join("config.toml");
let config_toml_content = fs::read_to_string(&config_toml).unwrap();
let config_toml_updated_content = config_toml_content.replace(
&previous_year.into_inner().to_string(),
&year.into_inner().to_string(),
);
fs::write(config_toml, config_toml_updated_content).unwrap();

// Update benchmarks in README.md
crate::template::readme_benchmarks::update_after_switch_year().unwrap();
}
8 changes: 1 addition & 7 deletions src/template/day.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
use chrono::{Datelike, FixedOffset, Utc};
use std::error::Error;
use std::fmt::Display;
use std::str::FromStr;

#[cfg(feature = "today")]
use chrono::{Datelike, FixedOffset, Utc};

#[cfg(feature = "today")]
const SERVER_UTC_OFFSET: i32 = -5;

/// A valid day number of advent (i.e. an integer in range 1 to 25).
Expand Down Expand Up @@ -41,10 +38,7 @@ impl Day {
pub fn into_inner(self) -> u8 {
self.0
}
}

#[cfg(feature = "today")]
impl Day {
/// Returns the current day if it's between the 1st and the 25th of december, `None` otherwise.
pub fn today() -> Option<Self> {
let offset = FixedOffset::east_opt(SERVER_UTC_OFFSET * 3600)?;
Expand Down
2 changes: 2 additions & 0 deletions src/template/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ pub mod commands;
pub mod runner;

pub use day::*;
pub use year::*;

mod day;
mod readme_benchmarks;
mod run_multi;
mod timings;
mod year;

pub const ANSI_ITALIC: &str = "\x1b[3m";
pub const ANSI_BOLD: &str = "\x1b[1m";
Expand Down
23 changes: 22 additions & 1 deletion src/template/readme_benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ fn locate_table(readme: &str) -> Result<TablePosition, Error> {
}

fn construct_table(prefix: &str, timings: Timings, total_millis: f64) -> String {
let header = format!("{prefix} Benchmarks");
let header = format!(
"{prefix} Benchmarks for {}",
std::env::var("AOC_YEAR").unwrap()
);

let mut lines: Vec<String> = vec![
MARKER.into(),
Expand Down Expand Up @@ -97,6 +100,24 @@ pub fn update(timings: Timings) -> Result<(), Error> {
Ok(())
}

fn remove_benchmarks() {
let path = "README.md";
let mut readme = String::from_utf8_lossy(&fs::read(path).unwrap()).to_string();
let positions = locate_table(&readme).unwrap();
readme.replace_range(positions.pos_start..positions.pos_end, MARKER);
fs::write(path, &readme).unwrap();
}

pub fn update_after_switch_year() -> Result<(), Error> {
let timings = Timings::read_from_file();
if timings.data.is_empty() {
remove_benchmarks();
Ok(())
} else {
update(timings)
}
}

#[cfg(feature = "test_lib")]
mod tests {
use super::{update_content, MARKER};
Expand Down
Loading