Skip to content

Commit

Permalink
Re-export year and util crates
Browse files Browse the repository at this point in the history
  • Loading branch information
ictrobot committed Aug 31, 2024
1 parent cc89ed1 commit 6b4e658
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 44 deletions.
4 changes: 3 additions & 1 deletion crates/aoc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
mod puzzles;
pub mod years;
mod years;

pub use ::utils;
pub use puzzles::{PuzzleFn, PUZZLES};
pub use years::*;
16 changes: 10 additions & 6 deletions crates/aoc/src/puzzles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,25 @@ use utils::{
pub type PuzzleFn = fn(&str) -> Result<(String, String), InputError>;

macro_rules! matcher {
($([$(::$p:ident)+])*) => {
($(
$y:literal => $year:ident{$(
$d:literal => $day:ident,
)*}
)*) => {
/// Constant containing each puzzle solution.
///
/// Each puzzle is represented by a tuple of [`Year`], [`Day`] and [`PuzzleFn`], which takes
/// a input string and returns the part 1 and 2 solutions as strings, or an [`InputError`].
///
/// Generated from [`all_puzzles!`].
pub const PUZZLES: &[(Year, Day, PuzzleFn)] = &[$(
($(::$p)+::YEAR, $(::$p)+::DAY, |input: &str| {
let solution = $(::$p)+::new(input, InputType::Real)?;
pub const PUZZLES: &[(Year, Day, PuzzleFn)] = &[$($(
(crate::$year::$day::YEAR, crate::$year::$day::DAY, |input: &str| {
let solution = crate::$year::$day::new(input, InputType::Real)?;
let part1 = solution.part1();
let part2 = solution.part2();
Ok((part1.to_string(), part2.to_string()))
})
),*];
}),
)*)*];
};
}
all_puzzles!(matcher);
67 changes: 37 additions & 30 deletions crates/aoc/src/years.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Implementation for [`all_puzzles!`](crate::all_puzzles!).
//!
//! Each of the re-exported macros below is either the `puzzles!` macro from the corresponding
//! crate if the corresponding feature is enabled, or the [`utils::puzzles_noop!`] no-op macro if
//! it is not. These macros are then chained together by [`all_puzzles!`](crate::all_puzzles!).
//! Each of the year crates is re-exported below if the corresponding feature is enabled, or a
//! placeholder crate with the [`utils::puzzles_noop!`] no-op `puzzles!` macro if it is not.
//! The `puzzles!` macros are then chained together by [`all_puzzles!`](crate::all_puzzles!).
//!
//! This main advantage of this approach over passing `#[cfg(feature = ?)]` attributes to the
//! callback is that it prevents disabled years from being expanded at all, which should speed up
Expand All @@ -12,23 +12,22 @@

// xtask update re-exports
#[cfg(not(feature = "year2015"))]
pub use ::utils::puzzles_noop as year2015;
pub mod year2015 {
pub use ::utils::puzzles_noop as puzzles;
}
#[cfg(not(feature = "year2016"))]
pub use ::utils::puzzles_noop as year2016;
pub mod year2016 {
pub use ::utils::puzzles_noop as puzzles;
}
#[cfg(feature = "year2015")]
pub use ::year2015::puzzles as year2015;
pub use ::year2015;
#[cfg(feature = "year2016")]
pub use ::year2016::puzzles as year2016;
pub use ::year2016;

/// Macro which invokes a callback macro with a list of all implemented puzzle solutions.
///
/// This macro chains re-exported `puzzles!` macros in [`aoc::years`](self), provided by each year
/// crate.
///
/// The callback macro will be called once, with all solutions, which allows generation of complete
/// match statements. The paths can be matched with `$([$(::$p:ident)+])*`. `$([$p:path])*` can also
/// be used, but it has [limitations](https://github.com/rust-lang/rust/issues/48067) making it far
/// less useful.
/// This macro chains `puzzles!` macros in the re-exported year modules. The callback macro will be
/// called once with all the solutions, which makes it easy to generate match statements or arrays.
///
/// Running `cargo xtask update` will automatically update the chain of year macros.
///
Expand All @@ -38,24 +37,28 @@ pub use ::year2016::puzzles as year2016;
///
/// ```
/// # use aoc::all_puzzles;
/// # use utils::{PuzzleExamples, Puzzle};
/// # use utils::PuzzleExamples;
/// # use utils::input::InputType;
/// #
/// macro_rules! callback {
/// ($([$(::$p:ident)+])*) => {$(
/// println!("{} {}", $(::$p)+::YEAR, $(::$p)+::DAY);///
/// for (input_str, p1, p2) in $(::$p)+::EXAMPLES.iter() {
/// let solution = $(::$p)+::new(input_str, InputType::Example).unwrap();
/// ($(
/// $y:literal => $year:ident{$(
/// $d:literal => $day:ident,
/// )*}
/// )*) => {$($(
/// println!("{} {}", $y, $d);
/// for (input_str, p1, p2) in aoc::$year::$day::EXAMPLES {
/// let solution = aoc::$year::$day::new(input_str, InputType::Example).unwrap();
/// println!(" parse({input_str}) = {solution:?}");
/// if (p1.is_some()) { println!(" part1(...) = {}", solution.part1()); }
/// if (p2.is_some()) { println!(" part2(...) = {}", solution.part2()); }
/// }
/// )*};
/// )*)*};
/// }
/// all_puzzles!(callback);
/// ```
///
/// Generating a match statement and passing extra arguments:
/// Generating a match statement:
///
/// ```
/// # use aoc::all_puzzles;
Expand All @@ -64,27 +67,31 @@ pub use ::year2016::puzzles as year2016;
/// #
/// fn example_count(year: Year, day: Day) -> Option<usize> {
/// macro_rules! callback {
/// ($year:ident $day:ident $([$(::$p:ident)+])*) => {
/// match ($year, $day) {
/// $(
/// ($(::$p)+::YEAR, $(::$p)+::DAY) => Some($(::$p)+::EXAMPLES.len()),
/// )*
/// ($(
/// $y:literal => $year:ident{$(
/// $d:literal => $day:ident,
/// )*}
/// )*) => {
/// match (year, day) {
/// $($(
/// (aoc::$year::$day::YEAR, aoc::$year::$day::DAY) => Some(aoc::$year::$day::EXAMPLES.len()),
/// )*)*
/// _ => None,
/// }
/// };
/// }
/// all_puzzles!{callback, year, day}
/// all_puzzles!{callback}
/// }
/// ```
#[allow(clippy::module_name_repetitions)] // Once exported name is aoc::all_puzzles
#[macro_export]
macro_rules! all_puzzles {
($callback:path $(,$arg:tt)*$(,)?) => {
::utils::puzzles_noop!{
$crate::utils::puzzles_noop!{
[
// xtask update all_puzzles
$crate::years::year2015,
$crate::years::year2016,
$crate::year2015::puzzles,
$crate::year2016::puzzles,

$callback
]
Expand Down
10 changes: 6 additions & 4 deletions crates/utils/src/framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ pub trait PuzzleExamples<P1: Debug + Display + 'static, P2: Debug + Display + 's
///
/// A `puzzle!` macro is defined and exported, which takes one or more callback macro paths and a
/// list of arguments captured as `tt` fragments. The macro expands to calling the first callback
/// with the remaining callback paths, the provided arguments and paths to all the day
/// structs defined in this crate. These macros are then chained across all year crates to implement
/// [`aoc::all_puzzles!`](../aoc/macro.all_puzzles.html).
/// with the remaining callback paths and the provided arguments followed by the year number, crate
/// name and a list of day numbers and structs. These macros are then chained across all year
/// crates to implement [`aoc::all_puzzles!`](../aoc/macro.all_puzzles.html).
///
/// Running `cargo xtask update` will automatically update the list of days inside macro invocations
/// in files matching `crates/year????/src/lib.rs`.
Expand Down Expand Up @@ -67,7 +67,9 @@ macro_rules! year {
$dollar callback!{
$dollar([$dollar($dollar callbacks),+])?
$dollar($dollar args)*
$([::$crate_name::$day_struct])+
$year => $crate_name{$(
$day => $day_struct,
)+}
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions crates/xtask/src/cmd/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ fn update_aoc_years_rs(aoc_dir: &Path, years: &[Year]) -> Result<(), Box<dyn Err
for &year in years {
write!(
&mut replacement,
"\n $crate::years::{},",
"\n $crate::{}::puzzles,",
year_create_name(year)
)?;
}
Expand All @@ -109,13 +109,15 @@ fn update_aoc_years_rs(aoc_dir: &Path, years: &[Year]) -> Result<(), Box<dyn Err
&mut year_reexports,
r#"
#[cfg(feature = "{crate_name}")]
pub use ::{crate_name}::puzzles as {crate_name};"#
pub use ::{crate_name};"#
)?;
write!(
&mut noop_reexports,
r#"
#[cfg(not(feature = "{crate_name}"))]
pub use ::utils::puzzles_noop as {crate_name};"#
pub mod {crate_name} {{
pub use ::utils::puzzles_noop as puzzles;
}}"#
)?;
}

Expand Down

0 comments on commit 6b4e658

Please sign in to comment.