diff --git a/.cargo/config.toml b/.cargo/config.toml index e963c04..21bb16c 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,4 @@ [alias] -scaffold = "run --bin scaffold --quiet --release --" -download = "run --bin download --quiet --release --" -download_all = "run --bin download_all --quiet --release --" - +scaffold = "run -p scaffold --release --quiet --" +download = "run -p download --release --quiet --" solve = "run --bin" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 00123ca..0000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Check - -on: push - -env: - CARGO_TERM_COLOR: always - -jobs: - check: - runs-on: ubuntu-latest - name: Check - steps: - - uses: actions/checkout@v3 - - name: cargo check - run: cargo check - test: - runs-on: ubuntu-latest - name: Test - steps: - - uses: actions/checkout@v3 - - name: cargo test - run: cargo test - clippy: - runs-on: ubuntu-latest - name: Lint (clippy) - steps: - - uses: actions/checkout@v3 - - name: cargo clippy - run: cargo clippy -- -D warnings - fmt: - runs-on: ubuntu-latest - name: Format - steps: - - uses: actions/checkout@v3 - - name: cargo fmt - run: cargo fmt --check diff --git a/.gitignore b/.gitignore index fd16741..d27a910 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,6 @@ target/ .env # downloaded inputs -/src/inputs/* -!/src/inputs/.keep +/data/inputs/* +!/data/inputs/.keep diff --git a/Cargo.lock b/Cargo.lock index 527d81c..f264e58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ahash" @@ -24,19 +24,15 @@ dependencies = [ [[package]] name = "aoc" -version = "24.0.0" +version = "48.0.0" dependencies = [ - "dotenv", "elves", "hashbrown 0.13.1", "hex-literal", "itertools", "lazy_static", "md-5", - "pico-args", "regex", - "reqwest", - "tokio", ] [[package]] @@ -47,9 +43,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" -version = "0.13.1" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" @@ -127,10 +123,19 @@ dependencies = [ ] [[package]] -name = "dotenv" -version = "0.15.0" +name = "dotenvy" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "download" +version = "48.0.0" +dependencies = [ + "dotenvy", + "pico-args", + "reqwest", +] [[package]] name = "either" @@ -212,6 +217,12 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + [[package]] name = "futures-sink" version = "0.3.25" @@ -231,9 +242,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ "futures-core", + "futures-io", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -280,15 +294,6 @@ dependencies = [ "ahash", ] -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - [[package]] name = "hex-literal" version = "0.3.4" @@ -437,16 +442,6 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.17" @@ -507,16 +502,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "once_cell" version = "1.16.0" @@ -568,29 +553,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys 0.42.0", -] - [[package]] name = "percent-encoding" version = "2.2.0" @@ -676,9 +638,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.13" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64", "bytes", @@ -698,9 +660,12 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -711,12 +676,30 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + [[package]] name = "ryu" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +[[package]] +name = "scaffold" +version = "48.0.0" +dependencies = [ + "dotenvy", + "pico-args", + "reqwest", +] + [[package]] name = "schannel" version = "0.1.20" @@ -727,12 +710,6 @@ dependencies = [ "windows-sys 0.36.1", ] -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "security-framework" version = "2.7.0" @@ -785,15 +762,6 @@ dependencies = [ "serde", ] -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - [[package]] name = "slab" version = "0.4.7" @@ -803,12 +771,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - [[package]] name = "socket2" version = "0.4.7" @@ -830,6 +792,33 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" version = "3.3.0" @@ -870,26 +859,11 @@ dependencies = [ "libc", "memchr", "mio", - "num_cpus", - "parking_lot", "pin-project-lite", - "signal-hook-registry", "socket2", - "tokio-macros", "windows-sys 0.42.0", ] -[[package]] -name = "tokio-macros" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tokio-native-tls" version = "0.3.0" @@ -1129,21 +1103,51 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.0", "windows_aarch64_msvc 0.42.0", "windows_i686_gnu 0.42.0", "windows_i686_msvc 0.42.0", "windows_x86_64_gnu 0.42.0", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.0", "windows_x86_64_msvc 0.42.0", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" @@ -1156,6 +1160,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -1168,6 +1178,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_msvc" version = "0.36.1" @@ -1180,6 +1196,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" @@ -1192,12 +1214,24 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -1211,10 +1245,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" [[package]] -name = "winreg" -version = "0.10.1" +name = "windows_x86_64_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] diff --git a/Cargo.toml b/Cargo.toml index f29719b..f79f1bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,30 @@ -[package] -name = "aoc" -description = "solutions for advent of code 2016" +[workspace.package] +description = "Advent of Code 2016" readme = "README.md" -version = "24.0.0" +version = "48.0.0" edition = "2021" license = "MIT" authors = ["Matej JaneΕΎič "] -repository = "https://github.com/janezicmatej/aoc-template.git" -default-run = "aoc" +repository = "https://git.janezic.dev/janezicmatej/aoc2016.git" + +[workspace] +members = ["utils/download", "utils/scaffold"] + +[package] +name = "aoc" +description.workspace = true +readme.workspace = true +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true [dependencies] -dotenv = "0.15.0" elves = "0.2.0" hashbrown = "0.13.1" hex-literal = "0.3.4" itertools = "0.10.5" lazy_static = "1.4.0" md-5 = "0.10.5" -pico-args = "0.5.0" regex = "1.7.0" -reqwest = "0.11.13" -tokio = { version = "1.23.0", features = ["full"] } diff --git a/README.md b/README.md index b9e22e7..6c2e9db 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,26 @@ -![ci](https://github.com/janezicmatej/aoc2016/actions/workflows/ci.yml/badge.svg) # Advent-of-Code 2016 *This is a dumbed down version of [fspoettel/advent-of-code-rust](https://github.com/fspoettel/advent-of-code-rust) with some extra features* ## Project overview ### Project structure +- `data/` : + - `examples/`: example files go here; you can push this as test are run in ci + - `inputs/`: this directory is gitignored, input files go here - `src/` : - `bin/`: - `.rs`: solution files - - `*.rs`: convenience scripts - - `inputs/`: this directory is gitignored, input files go here - - `examples/`: example files go here; you can push this as test are run in ci - - `utils/`: utils files go here - - `lib.rs`: contains framework code - - `main.rs`: contains framework code + - `lib.rs`: library entrypoint, reusable code goes here + - `template.rs`: contains template code +- `utils/`: binary packages with convenience scripts structured using cargo workspaces - `.env.example`: example dotenv file ### Cli - `cargo scaffold `: prepare solution files for `day` - `cargo download `: download input file for `day` - `cargo solve `: run solution against input for `day` -- `cargo all`: alias for run; runs solutions for all days +*Run `cargo build --workspace --release` once so scaffold and download packages get compiled, otherwise they will have to be compiled on first run.* ### dotenv diff --git a/src/examples/.keep b/data/examples/.keep similarity index 100% rename from src/examples/.keep rename to data/examples/.keep diff --git a/src/examples/01.txt b/data/examples/01.txt similarity index 100% rename from src/examples/01.txt rename to data/examples/01.txt diff --git a/src/examples/02.txt b/data/examples/02.txt similarity index 100% rename from src/examples/02.txt rename to data/examples/02.txt diff --git a/src/examples/03.txt b/data/examples/03.txt similarity index 100% rename from src/examples/03.txt rename to data/examples/03.txt diff --git a/src/examples/04.txt b/data/examples/04.txt similarity index 100% rename from src/examples/04.txt rename to data/examples/04.txt diff --git a/src/examples/05.txt b/data/examples/05.txt similarity index 100% rename from src/examples/05.txt rename to data/examples/05.txt diff --git a/src/examples/06.txt b/data/examples/06.txt similarity index 100% rename from src/examples/06.txt rename to data/examples/06.txt diff --git a/src/examples/07.txt b/data/examples/07.txt similarity index 100% rename from src/examples/07.txt rename to data/examples/07.txt diff --git a/src/examples/08.txt b/data/examples/08.txt similarity index 100% rename from src/examples/08.txt rename to data/examples/08.txt diff --git a/src/examples/09.txt b/data/examples/09.txt similarity index 100% rename from src/examples/09.txt rename to data/examples/09.txt diff --git a/src/inputs/.keep b/data/inputs/.keep similarity index 100% rename from src/inputs/.keep rename to data/inputs/.keep diff --git a/src/bin/.keep b/src/bin/.keep new file mode 100644 index 0000000000..e69de29 diff --git a/src/bin/01.rs b/src/bin/01.rs index 6a50228..dc4a16b 100644 --- a/src/bin/01.rs +++ b/src/bin/01.rs @@ -76,21 +76,20 @@ pub fn part_two(input: &str) -> Option { None } -fn main() { - let input = &aoc::read_file("inputs", 1); - aoc::solve!(part_one, part_two, input); -} + +aoc::solution!(1); + #[cfg(test)] mod tests { use super::*; #[test] fn test_part_one() { - let input = aoc::read_file("examples", 1); + let input = aoc::template::read_file("examples", 1); assert_eq!(part_one(&input), Some(8)); } #[test] fn test_part_two() { - let input = aoc::read_file("examples", 1); + let input = aoc::template::read_file("examples", 1); assert_eq!(part_two(&input), Some(4)); } } diff --git a/src/bin/02.rs b/src/bin/02.rs index c50894f..fcb94ce 100644 --- a/src/bin/02.rs +++ b/src/bin/02.rs @@ -58,21 +58,20 @@ pub fn part_two(input: &str) -> Option { Some(res) } -fn main() { - let input = &aoc::read_file("inputs", 2); - aoc::solve!(part_one, part_two, input); -} + +aoc::solution!(2); + #[cfg(test)] mod tests { use super::*; #[test] fn test_part_one() { - let input = aoc::read_file("examples", 2); + let input = aoc::template::read_file("examples", 2); assert_eq!(part_one(&input), Some(1985)); } #[test] fn test_part_two() { - let input = aoc::read_file("examples", 2); + let input = aoc::template::read_file("examples", 2); assert_eq!(part_two(&input), Some("5DB3".to_string())); } } diff --git a/src/bin/03.rs b/src/bin/03.rs index 77b357b..38b92f9 100644 --- a/src/bin/03.rs +++ b/src/bin/03.rs @@ -41,21 +41,20 @@ pub fn part_two(input: &str) -> Option { .count(), ) } -fn main() { - let input = &aoc::read_file("inputs", 3); - aoc::solve!(part_one, part_two, input); -} + +aoc::solution!(3); + #[cfg(test)] mod tests { use super::*; #[test] fn test_part_one() { - let input = aoc::read_file("examples", 3); + let input = aoc::template::read_file("examples", 3); assert_eq!(part_one(&input), Some(2)); } #[test] fn test_part_two() { - let input = aoc::read_file("examples", 3); + let input = aoc::template::read_file("examples", 3); assert_eq!(part_two(&input), Some(1)); } } diff --git a/src/bin/04.rs b/src/bin/04.rs index 9445b77..7e10795 100644 --- a/src/bin/04.rs +++ b/src/bin/04.rs @@ -77,21 +77,20 @@ pub fn part_two(input: &str) -> Option { None } -fn main() { - let input = &aoc::read_file("inputs", 4); - aoc::solve!(part_one, part_one, input); -} + +aoc::solution!(4); + #[cfg(test)] mod tests { use super::*; #[test] fn test_part_one() { - let input = aoc::read_file("examples", 4); + let input = aoc::template::read_file("examples", 4); assert_eq!(part_one(&input), Some(1514)); } #[test] fn test_part_two() { - let input = aoc::read_file("examples", 4); + let input = aoc::template::read_file("examples", 4); assert_eq!(part_two(&input), None); } } diff --git a/src/bin/05.rs b/src/bin/05.rs index cfafbd1..442731b 100644 --- a/src/bin/05.rs +++ b/src/bin/05.rs @@ -57,21 +57,20 @@ pub fn part_two(input: &str) -> Option { .collect(), ) } -fn main() { - let input = &aoc::read_file("inputs", 5); - aoc::solve!(part_one, part_two, input); -} + +aoc::solution!(5); + #[cfg(test)] mod tests { use super::*; #[test] fn test_part_one() { - let input = aoc::read_file("examples", 5); + let input = aoc::template::read_file("examples", 5); assert_eq!(part_one(&input), Some("18f47a30".to_string())); } #[test] fn test_part_two() { - let input = aoc::read_file("examples", 5); + let input = aoc::template::read_file("examples", 5); assert_eq!(part_two(&input), Some("05ace8e3".to_string())); } } diff --git a/src/bin/06.rs b/src/bin/06.rs index d0d8b70..dd71d0b 100644 --- a/src/bin/06.rs +++ b/src/bin/06.rs @@ -28,21 +28,20 @@ pub fn part_one(input: &str) -> Option { pub fn part_two(input: &str) -> Option { Some(solve(input, true)) } -fn main() { - let input = &aoc::read_file("inputs", 6); - aoc::solve!(part_one, part_two, input); -} + +aoc::solution!(6); + #[cfg(test)] mod tests { use super::*; #[test] fn test_part_one() { - let input = aoc::read_file("examples", 6); + let input = aoc::template::read_file("examples", 6); assert_eq!(part_one(&input), Some("easter".to_string())); } #[test] fn test_part_two() { - let input = aoc::read_file("examples", 6); + let input = aoc::template::read_file("examples", 6); assert_eq!(part_two(&input), Some("advent".to_string())); } } diff --git a/src/bin/07.rs b/src/bin/07.rs index cabd663..8bd854c 100644 --- a/src/bin/07.rs +++ b/src/bin/07.rs @@ -62,21 +62,20 @@ pub fn part_two(input: &str) -> Option { Some(count) } -fn main() { - let input = &aoc::read_file("inputs", 7); - aoc::solve!(part_one, part_two, input); -} + +aoc::solution!(7); + #[cfg(test)] mod tests { use super::*; #[test] fn test_part_one() { - let input = aoc::read_file("examples", 7); + let input = aoc::template::read_file("examples", 7); assert_eq!(part_one(&input), Some(2)); } #[test] fn test_part_two() { - let input = aoc::read_file("examples", 7); + let input = aoc::template::read_file("examples", 7); assert_eq!(part_two(&input), Some(3)); } } diff --git a/src/bin/08.rs b/src/bin/08.rs index 720f403..941d036 100644 --- a/src/bin/08.rs +++ b/src/bin/08.rs @@ -106,16 +106,14 @@ pub fn part_two(input: &str) -> Option { Some(s) } -fn main() { - let input = &aoc::read_file("inputs", 8); - aoc::solve!(part_one, part_two, input); -} +aoc::solution!(8); + #[cfg(test)] mod tests { use super::*; #[test] fn test_part_one() { - let input = aoc::read_file("examples", 8); + let input = aoc::template::read_file("examples", 8); assert_eq!(part_one(&input), Some(6)); } } diff --git a/src/bin/09.rs b/src/bin/09.rs index 711c185..3407c9e 100644 --- a/src/bin/09.rs +++ b/src/bin/09.rs @@ -59,21 +59,20 @@ pub fn part_one(input: &str) -> Option { pub fn part_two(input: &str) -> Option { Some(decompress(input, true)) } -fn main() { - let input = &aoc::read_file("inputs", 9); - aoc::solve!(part_one, part_two, input); -} + +aoc::solution!(9); + #[cfg(test)] mod tests { use super::*; #[test] fn test_part_one() { - let input = aoc::read_file("examples", 9); + let input = aoc::template::read_file("examples", 9); assert_eq!(part_one(&input), Some(238)); } #[test] fn test_part_two() { - let input = aoc::read_file("examples", 9); + let input = aoc::template::read_file("examples", 9); assert_eq!(part_two(&input), Some(445)); } } diff --git a/src/bin/download_all.rs b/src/bin/download_all.rs deleted file mode 100644 index 3d0bb37..0000000000 --- a/src/bin/download_all.rs +++ /dev/null @@ -1,57 +0,0 @@ -use dotenv::dotenv; -use itertools::Itertools; -use reqwest::{header, Client}; -use std::{env, fs::OpenOptions, io::Write, process}; - -#[tokio::main] -async fn main() { - dotenv().ok(); - let token = env::var("TOKEN").expect("$TOKEN is not set"); - let year = env::var("YEAR") - .expect("$YEAR is not set") - .parse::() - .expect("$YEAR must be a number"); - - let mut headers = header::HeaderMap::new(); - let mut session_header = header::HeaderValue::from_str(format!("session={token}").as_str()) - .expect("Error building cookie header"); - session_header.set_sensitive(true); - headers.insert(header::COOKIE, session_header); - - let client = Client::builder().default_headers(headers).build().unwrap(); - let responses = (1..=25) - .map(|d| { - let endpoint = format!("https://adventofcode.com/{year}/day/{d}/input"); - println!("{endpoint}"); - client.get(endpoint).send() - }) - .collect_vec() - .into_iter() - .map(|x| async { x.await.unwrap().text().await.unwrap() }) - .collect_vec(); - - for (day, res) in (1..=25).zip(responses) { - let input_path = format!("src/inputs/{day:02}.txt"); - let mut file = match OpenOptions::new() - .write(true) - .create(true) - .open(&input_path) - { - Ok(file) => file, - Err(e) => { - eprintln!("Failed to create module file: {e}"); - process::exit(1); - } - }; - - match file.write_all(res.await.as_bytes()) { - Ok(_) => { - println!("Downloaded input file \"{}\"", &input_path); - } - Err(e) => { - eprintln!("Failed to write module contents: {e}"); - process::exit(1); - } - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 9e1150e..612b5b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,163 +1 @@ -/* - * This file contains template code. - * There is no need to edit this file unless you want to change template functionality. - * Prefer `./helpers.rs` if you want to extract code from your solutions. - */ -use std::env; -use std::fs; - -pub const ANSI_ITALIC: &str = "\x1b[3m"; -pub const ANSI_BOLD: &str = "\x1b[1m"; -pub const ANSI_RESET: &str = "\x1b[0m"; - -#[macro_export] -macro_rules! solve { - ($solver_one:ident, $solver_two:ident, $input:expr) => { - use aoc::{ANSI_BOLD, ANSI_ITALIC, ANSI_RESET}; - use std::fmt::Display; - use std::str::FromStr; - use std::time::Instant; - - fn print_result( - func: impl FnOnce(&str) -> Option, - part: u8, - input: &str, - quiet: bool, - ) { - if quiet { - let result = func(input); - if let Some(r) = result { - println!("{}", r); - } - return; - } - - println!("πŸŽ„ {}Part {}{} πŸŽ„", ANSI_BOLD, part, ANSI_RESET); - - let timer = Instant::now(); - let result = func(input); - let elapsed = timer.elapsed(); - match result { - Some(result) => { - println!( - "{} {}(elapsed: {:.2?}){}", - result, ANSI_ITALIC, elapsed, ANSI_RESET - ); - } - None => { - println!("not solved.") - } - } - } - - let mut args = pico_args::Arguments::from_env(); - - let part = args - .opt_value_from_str("-p") - .expect("There was an error parsing cli args."); - let quiet = args.contains("-q"); - - match part { - None => { - print_result($solver_one, 1, $input, quiet); - print_result($solver_two, 2, $input, quiet); - } - Some(pp) => match pp { - 1 => { - print_result($solver_one, 1, $input, quiet); - } - 2 => { - print_result($solver_two, 2, $input, quiet); - } - _ => eprintln!("error: expected -p to be 1 or 2"), - }, - } - }; -} - -pub fn read_file(folder: &str, day: u8) -> String { - let cwd = env::current_dir().unwrap(); - - let filepath = cwd.join("src").join(folder).join(format!("{day:02}.txt")); - - let f = fs::read_to_string(filepath); - f.expect("could not open input file").trim().to_string() -} - -fn parse_time(val: &str, postfix: &str) -> f64 { - val.split(postfix).next().unwrap().parse().unwrap() -} - -pub fn parse_args() -> Result { - let mut args = pico_args::Arguments::from_env(); - args.free_from_str() -} - -pub fn parse_exec_time(output: &str) -> f64 { - output.lines().fold(0_f64, |acc, l| { - if !l.contains("elapsed:") { - acc - } else { - let timing = l.split("(elapsed: ").last().unwrap(); - // use `contains` istd. of `ends_with`: string may contain ANSI escape sequences. - // for possible time formats, see: https://github.com/rust-lang/rust/blob/1.64.0/library/core/src/time.rs#L1176-L1200 - if timing.contains("ns)") { - acc // range below rounding precision. - } else if timing.contains("Β΅s)") { - acc + parse_time(timing, "Β΅s") / 1000_f64 - } else if timing.contains("ms)") { - acc + parse_time(timing, "ms") - } else if timing.contains("s)") { - acc + parse_time(timing, "s") * 1000_f64 - } else { - acc - } - } - }) -} - -/// copied from: https://github.com/rust-lang/rust/blob/1.64.0/library/std/src/macros.rs#L328-L333 -#[cfg(test)] -macro_rules! assert_approx_eq { - ($a:expr, $b:expr) => {{ - let (a, b) = (&$a, &$b); - assert!( - (*a - *b).abs() < 1.0e-6, - "{} is not approximately equal to {}", - *a, - *b - ); - }}; -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_parse_exec_time() { - assert_approx_eq!( - parse_exec_time(&format!( - "πŸŽ„ Part 1 πŸŽ„\n0 (elapsed: 74.13ns){ANSI_RESET}\nπŸŽ„ Part 2 πŸŽ„\n0 (elapsed: 50.00ns){ANSI_RESET}" - )), - 0_f64 - ); - - assert_approx_eq!( - parse_exec_time("πŸŽ„ Part 1 πŸŽ„\n0 (elapsed: 755Β΅s)\nπŸŽ„ Part 2 πŸŽ„\n0 (elapsed: 700Β΅s)"), - 1.455_f64 - ); - - assert_approx_eq!( - parse_exec_time("πŸŽ„ Part 1 πŸŽ„\n0 (elapsed: 70Β΅s)\nπŸŽ„ Part 2 πŸŽ„\n0 (elapsed: 1.45ms)"), - 1.52_f64 - ); - - assert_approx_eq!( - parse_exec_time( - "πŸŽ„ Part 1 πŸŽ„\n0 (elapsed: 10.3s)\nπŸŽ„ Part 2 πŸŽ„\n0 (elapsed: 100.50ms)" - ), - 10400.50_f64 - ); - } -} +pub mod template; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 45bca4c..0000000000 --- a/src/main.rs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file contains template code. - * There is no need to edit this file unless you want to change template functionality. - */ -use aoc::{ANSI_BOLD, ANSI_ITALIC, ANSI_RESET}; -use std::process::Command; - -fn main() { - let total: f64 = (1..=25) - .map(|day| { - let day = format!("{day:02}"); - - let cmd = Command::new("cargo") - .args(["run", "--release", "--bin", &day]) - .output() - .unwrap(); - - println!("----------"); - println!("{ANSI_BOLD}| Day {day} |{ANSI_RESET}"); - println!("----------"); - - let output = String::from_utf8(cmd.stdout).unwrap(); - let is_empty = output.is_empty(); - - println!( - "{}", - if is_empty { - "Not solved." - } else { - output.trim() - } - ); - - if is_empty { - 0_f64 - } else { - aoc::parse_exec_time(&output) - } - }) - .sum(); - - println!("{ANSI_BOLD}Total:{ANSI_RESET} {ANSI_ITALIC}{total:.2}ms{ANSI_RESET}"); -} diff --git a/src/template.rs b/src/template.rs new file mode 100644 index 0000000000..d1dc75a --- /dev/null +++ b/src/template.rs @@ -0,0 +1,56 @@ +/* + * This file contains template code. + * There is no need to edit this file unless you want to change template functionality. + */ + +use std::env; +use std::fs; + +pub const ANSI_ITALIC: &str = "\x1b[3m"; +pub const ANSI_BOLD: &str = "\x1b[1m"; +pub const ANSI_RESET: &str = "\x1b[0m"; + +use std::fmt::Display; +use std::time::Duration; +use std::time::Instant; + +fn time_solution(func: impl FnOnce(&str) -> Option, input: &str) -> Option<(T, Duration)> { + let timer = Instant::now(); + let result = func(input); + let elapsed = timer.elapsed(); + + result.map(|result| (result, elapsed)) +} + +pub fn print_result(func: impl FnOnce(&str) -> Option, input: &str, part: u8) { + match time_solution(func, input) { + Some((result, elapsed)) => { + println!( + "{}Part {}{}: {} {}(elapsed: {:.2?}){}", + ANSI_BOLD, part, ANSI_RESET, result, ANSI_ITALIC, elapsed, ANSI_RESET + ); + } + None => { + println!("{}Part {}{}: not solved.", ANSI_BOLD, part, ANSI_RESET) + } + } +} + +#[macro_export] +macro_rules! solution { + ($day:expr) => { + fn main() { + let input = aoc::template::read_file("inputs", $day); + aoc::template::print_result(part_one, &input, 1); + aoc::template::print_result(part_two, &input, 2); + } + }; +} + +#[must_use] +pub fn read_file(folder: &str, day: u8) -> String { + let cwd = env::current_dir().unwrap(); + let filepath = cwd.join("data").join(folder).join(format!("{day:02}.txt")); + let f = fs::read_to_string(filepath); + f.expect("could not open input file").trim().to_string() +} diff --git a/utils/download/Cargo.toml b/utils/download/Cargo.toml new file mode 100644 index 0000000000..ddc6fe1 --- /dev/null +++ b/utils/download/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "download" +description.workspace = true +readme.workspace = true +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true + +[dependencies] +reqwest = { version = "0.11.22", features = ["blocking"] } +dotenvy = "0.15.6" +pico-args = "0.5.0" diff --git a/src/bin/download.rs b/utils/download/src/main.rs similarity index 83% rename from src/bin/download.rs rename to utils/download/src/main.rs index dba71ee..371e5ec 100644 --- a/src/bin/download.rs +++ b/utils/download/src/main.rs @@ -1,10 +1,15 @@ -use dotenv::dotenv; -use reqwest::{header, Client}; +use dotenvy::dotenv; +use reqwest::blocking::Client; +use reqwest::header; use std::{env, fs::OpenOptions, io::Write, process}; -#[tokio::main] -async fn main() { - let day: u8 = match aoc::parse_args() { +pub fn parse_args() -> Result { + let mut args = pico_args::Arguments::from_env(); + args.free_from_str() +} + +fn main() { + let day: u8 = match parse_args() { Ok(day) => day, Err(_) => { eprintln!("Need to specify a day (as integer). example: `cargo download 7`"); @@ -30,13 +35,11 @@ async fn main() { let res = client .get(format!("https://adventofcode.com/{year}/day/{day}/input")) .send() - .await .unwrap() .text() - .await .unwrap(); - let input_path = format!("src/inputs/{day_padded}.txt"); + let input_path = format!("data/inputs/{day_padded}.txt"); let mut file = match OpenOptions::new() .write(true) .create(true) diff --git a/utils/scaffold/Cargo.toml b/utils/scaffold/Cargo.toml new file mode 100644 index 0000000000..169dff7 --- /dev/null +++ b/utils/scaffold/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "scaffold" +description.workspace = true +readme.workspace = true +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true + +[dependencies] +reqwest = { version = "0.11.22", features = ["blocking"] } +dotenvy = "0.15.6" +pico-args = "0.5.0" diff --git a/src/bin/scaffold.rs b/utils/scaffold/src/main.rs similarity index 73% rename from src/bin/scaffold.rs rename to utils/scaffold/src/main.rs index 341621c..4e5a705 100644 --- a/src/bin/scaffold.rs +++ b/utils/scaffold/src/main.rs @@ -8,31 +8,34 @@ use std::{ process, }; -const MODULE_TEMPLATE: &str = r###"pub fn part_one(input: &str) -> Option { +const MODULE_TEMPLATE: &str = r#"pub fn part_one(input: &str) -> Option { None } -pub fn part_two(input: &str) -> Option { + +pub fn part_two(input: &str) -> Option { None } -fn main() { - let input = &aoc::read_file("inputs", DAY); - aoc::solve!(part_one, part_two, input); -} + +aoc::solution!(DAY); + #[cfg(test)] mod tests { use super::*; #[test] fn test_part_one() { - let input = aoc::read_file("examples", DAY); - assert_eq!(part_one(&input), None); + assert_eq!(part_one(&aoc::template::read_file("examples", DAY)), None); } #[test] fn test_part_two() { - let input = aoc::read_file("examples", DAY); - assert_eq!(part_two(&input), None); + assert_eq!(part_two(&aoc::template::read_file("examples", DAY)), None); } } -"###; +"#; + +pub fn parse_args() -> Result { + let mut args = pico_args::Arguments::from_env(); + args.free_from_str() +} fn safe_create_file(path: &str) -> Result { OpenOptions::new().write(true).create_new(true).open(path) @@ -43,7 +46,7 @@ fn create_file(path: &str) -> Result { } fn main() { - let day = match aoc::parse_args() { + let day = match parse_args() { Ok(day) => day, Err(_) => { eprintln!("Need to specify a day (as integer). example: `cargo scaffold 7`"); @@ -53,8 +56,8 @@ fn main() { let day_padded = format!("{day:02}"); - let input_path = format!("src/inputs/{day_padded}.txt"); - let example_path = format!("src/examples/{day_padded}.txt"); + let input_path = format!("data/inputs/{day_padded}.txt"); + let example_path = format!("data/examples/{day_padded}.txt"); let module_path = format!("src/bin/{day_padded}.rs"); let mut file = match safe_create_file(&module_path) { @@ -96,8 +99,5 @@ fn main() { } println!("---"); - println!( - "πŸŽ„ Type `cargo solve {}` to run your solution.", - &day_padded - ); + println!("Type `cargo solve {}` to run your solution.", &day_padded); }