This commit is contained in:
LordMZTE 2021-12-04 23:24:26 +01:00
commit 5b84356e5e
6 changed files with 192 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
target/
Cargo.lock

5
Cargo.toml Normal file
View File

@ -0,0 +1,5 @@
[workspace]
members = [
"libaoc",
"libaoc-macros",
]

14
libaoc-macros/Cargo.toml Normal file
View File

@ -0,0 +1,14 @@
[package]
name = "libaoc-macros"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
proc-macro = true
[dependencies]
quote = "1.0"
syn = { version = "1.0", features = ["full"] }
[features]

108
libaoc-macros/src/lib.rs Normal file
View File

@ -0,0 +1,108 @@
#![feature(proc_macro_diagnostic)]
use proc_macro::TokenStream;
use quote::quote;
use syn::{
parse::{Parse, ParseStream},
parse_macro_input, parse_quote,
punctuated::Punctuated,
spanned::Spanned,
token::Comma,
Expr, Item, ItemMod,
};
#[proc_macro_attribute]
pub fn day(args: TokenStream, input: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(input as ItemMod);
let args = parse_macro_input!(args as Args);
let content = if let Some(c) = &mut input.content {
&mut c.1
} else {
input.span().unwrap().error("Module must have body!").emit();
return quote!(#input).into();
};
let fns = content
.iter()
.filter_map(|i| match i {
Item::Fn(fun) => Some(fun),
_ => None,
})
.collect::<Vec<_>>();
let part1 = fns
.iter()
.find(|i| &i.sig.ident.to_string() == "part1")
.copied();
let part2 = fns
.iter()
.find(|i| &i.sig.ident.to_string() == "part2")
.copied();
if part1.is_none() {
input
.span()
.unwrap()
.error("Need part1 function, which is not found within module.")
.emit();
return quote!(#input).into();
}
let part2 = if part2.is_some() {
quote!(Some(self::part2))
} else {
quote!(None)
};
let name = args.name;
content.push(parse_quote! {
pub fn aoc_day() -> libaoc::Day {
libaoc::Day {
name: format!("{}", #name),
part1: self::part1,
part2: #part2,
}
}
});
if let Some(infile) = args.input {
content.push(parse_quote!{
const INPUT: &str = include_str!(#infile);
});
}
quote!(#input).into()
}
struct Args {
name: Expr,
input: Option<Expr>,
}
impl Parse for Args {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut puncted = Punctuated::<Expr, Comma>::parse_separated_nonempty(input)?.into_iter();
let name = puncted.next();
if name.is_none() {
return Err(syn::Error::new(
input.span(),
"Must provide at least one argument for the day name!",
));
}
let input = puncted.next();
if let Some(excess) = puncted.next() {
return Err(syn::Error::new(
excess.span(),
"No more than 2 arguments allowed!",
));
}
Ok(Self {
name: name.unwrap(),
input,
})
}
}

11
libaoc/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "libaoc"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libaoc-macros = { path = "../libaoc-macros" }
miette = { version = "3.2", features = ["fancy"] }
owo-colors = "2.1"

52
libaoc/src/lib.rs Normal file
View File

@ -0,0 +1,52 @@
pub use libaoc_macros::*;
pub use miette;
use miette::Context;
use owo_colors::OwoColorize;
#[macro_export]
macro_rules! run_days {
($($day:ident),+) => {
libaoc::AocRunner::new()
$(.day($day::aoc_day()))*
.run()
};
}
pub struct Day {
pub name: String,
pub part1: fn() -> miette::Result<()>,
pub part2: Option<fn() -> miette::Result<()>>,
}
pub struct AocRunner {
days: Vec<Day>,
}
impl AocRunner {
pub fn new() -> Self {
Self { days: vec![] }
}
pub fn day(&mut self, day: Day) -> &mut Self {
self.days.push(day);
self
}
pub fn run(&self) -> miette::Result<()> {
for day in &self.days {
println!("Running day {}...", day.name.blue());
println!("{}", "--- Part 1 ---".red());
(day.part1)().wrap_err_with(|| format!("Running part 1 of day {}", &day.name))?;
println!();
if let Some(p2) = day.part2 {
println!("{}", "--- Part 2 ---".red());
p2().wrap_err_with(|| format!("Running part 2 of day {}", &day.name))?;
println!();
}
}
Ok(())
}
}