diff --git a/src/template.txt b/src/template.txt index 11344df..c010752 100644 --- a/src/template.txt +++ b/src/template.txt @@ -1,4 +1,4 @@ -advent_of_code::solution!(%DAY_NUMBER%); +advent_of_code::solution!(); pub fn part_one(input: &str) -> Option { None diff --git a/src/template/commands/scaffold.rs b/src/template/commands/scaffold.rs index fc3d950..a314372 100644 --- a/src/template/commands/scaffold.rs +++ b/src/template/commands/scaffold.rs @@ -40,11 +40,7 @@ pub fn handle(day: Day, overwrite: bool) { } }; - match file.write_all( - MODULE_TEMPLATE - .replace("%DAY_NUMBER%", &day.into_inner().to_string()) - .as_bytes(), - ) { + match file.write_all(MODULE_TEMPLATE.as_bytes()) { Ok(()) => { println!("Created module file \"{}\"", &module_path); } diff --git a/src/template/day.rs b/src/template/day.rs index 99b8280..7124baf 100644 --- a/src/template/day.rs +++ b/src/template/day.rs @@ -24,7 +24,7 @@ pub struct Day(u8); impl Day { /// Creates a [`Day`] from the provided value if it's in the valid range, /// returns [`None`] otherwise. - pub fn new(day: u8) -> Option { + pub const fn new(day: u8) -> Option { if day == 0 || day > 25 { return None; } @@ -150,6 +150,58 @@ macro_rules! day { }}; } +/// Creates a [`Day`] using the current file name. +/// +/// The file name must be a valid integer in the range 1-25. +#[macro_export] +macro_rules! day_from_file_name { + () => { + const { + let mut path = ::core::panic::Location::caller().file().as_bytes(); + + // Get the last part of the path (e.g. "11.rs") + // path = path.rsplit_once(|c| c == '\\' || c == '/').map(|(_, s)| s).unwrap_or(path) + let mut i = path.len(); + while i > 0 { + let c = path[i - 1]; + if c == b'\\' || c == b'/' { + path = path.split_at(i).1; + break; + } + + i -= 1; + } + + // Remove the extension + // path = path.split_once('.').map(|(s, _)| s).unwrap_or(path) + let mut i = 0; + while i < path.len() { + if path[i] == b'.' { + path = path.split_at(i).0; + break; + } + + i += 1; + } + + // Convert the path back into a &str + // Note: as we only split at ascii chars (/, \ or .) the path should be a valid &str + let path = match ::core::str::from_utf8(path) { + Ok(path) => path, + Err(_) => unreachable!(), + }; + + let day = match ::core::primitive::u8::from_str_radix(path, 10) { + Ok(day) => day, + Err(_) => panic!("the file name is expected to be a number"), + }; + + $crate::template::Day::new(day) + .expect("the file name should be a number between 1 and 25") + } + }; +} + /* -------------------------------------------------------------------------- */ #[cfg(feature = "test_lib")] diff --git a/src/template/mod.rs b/src/template/mod.rs index dd8e4c0..a83b35f 100644 --- a/src/template/mod.rs +++ b/src/template/mod.rs @@ -42,18 +42,31 @@ pub fn read_file_part(folder: &str, day: Day, part: u8) -> String { #[macro_export] macro_rules! solution { ($day:expr) => { - $crate::solution!(@impl $day, [part_one, 1] [part_two, 2]); + $crate::solution!(@impl $crate::day!($day), [part_one, 1] [part_two, 2]); }; ($day:expr, 1) => { - $crate::solution!(@impl $day, [part_one, 1]); + $crate::solution!(@impl $crate::day!($day), [part_one, 1]); }; ($day:expr, 2) => { - $crate::solution!(@impl $day, [part_two, 2]); + $crate::solution!(@impl $crate::day!($day), [part_two, 2]); + }; + + () => { + $crate::solution!(@impl $crate::day_from_file_name!(), [part_one, 1] [part_two, 2]); + }; + (*) => { + $crate::solution!(); + }; + (*, 1) => { + $crate::solution!(@impl $crate::day_from_file_name!(), [part_one, 1]); + }; + (*, 2) => { + $crate::solution!(@impl $crate::day_from_file_name!(), [part_two, 2]); }; (@impl $day:expr, $( [$func:expr, $part:expr] )*) => { /// The current day. - const DAY: $crate::template::Day = $crate::day!($day); + const DAY: $crate::template::Day = $day; #[cfg(feature = "dhat-heap")] #[global_allocator]