init
This commit is contained in:
commit
5b84356e5e
6 changed files with 192 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
target/
|
||||
Cargo.lock
|
5
Cargo.toml
Normal file
5
Cargo.toml
Normal file
|
@ -0,0 +1,5 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"libaoc",
|
||||
"libaoc-macros",
|
||||
]
|
14
libaoc-macros/Cargo.toml
Normal file
14
libaoc-macros/Cargo.toml
Normal 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
108
libaoc-macros/src/lib.rs
Normal 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
11
libaoc/Cargo.toml
Normal 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
52
libaoc/src/lib.rs
Normal 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(())
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue