2020: switch to anyhow

This commit is contained in:
Antoine Martin 2020-12-14 18:08:16 +01:00
parent f148347e5b
commit d91165207f
18 changed files with 161 additions and 179 deletions

3
Cargo.lock generated
View file

@ -9,6 +9,9 @@ checksum = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4"
[[package]] [[package]]
name = "aoc" name = "aoc"
version = "0.1.0" version = "0.1.0"
dependencies = [
"anyhow",
]
[[package]] [[package]]
name = "aoc2015" name = "aoc2015"

View file

@ -7,3 +7,4 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
anyhow = "1.0"

View file

@ -1,12 +1,6 @@
use std::env; use std::env;
pub type Error = Box<dyn std::error::Error>; use anyhow::{Context, Result};
pub type Result<T> = std::result::Result<T, Error>;
#[macro_export]
macro_rules! err {
($($string:expr),+) => (Box::<dyn std::error::Error>::from(format!($($string),+)))
}
pub type DayFunc = fn() -> Result<String>; pub type DayFunc = fn() -> Result<String>;
@ -16,15 +10,15 @@ pub fn run(days: &[DayFunc]) -> Result<()> {
match args.next() { match args.next() {
Some(arg) => { Some(arg) => {
let day: usize = arg.parse().expect("Please provide a day number"); let day: usize = arg.parse().context("couldn't parse day number")?;
let res = days[day - 1]().map_err(|e| err!("error running day specified: {}", e))?; let res = days[day - 1]().context("error running day specified")?;
println!("{}", res); println!("{}", res);
} }
None => { None => {
for (i, day) in days.iter().enumerate() { for (i, day) in days.iter().enumerate() {
let i = i + 1; let i = i + 1;
println!("day{}: ", i); println!("day{}: ", i);
let res = day().map_err(|e| err!("error running day {}: {}", i, e))?; let res = day().with_context(|| format!("error running day {}", i))?;
println!("{}", res); println!("{}", res);
} }
} }

View file

@ -1,7 +1,7 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::Write; use std::fmt::Write;
use aoc::{err, Result}; use anyhow::{anyhow, Result};
const INPUT: &str = include_str!("../input/day01.txt"); const INPUT: &str = include_str!("../input/day01.txt");
@ -17,7 +17,7 @@ pub fn run() -> Result<String> {
fn part1(input: &str) -> Result<i64> { fn part1(input: &str) -> Result<i64> {
let entries = input let entries = input
.lines() .lines()
.map(|line| line.parse::<i64>().map_err(|e| err!("{}", e))) .map(|line| line.parse::<i64>().map_err(anyhow::Error::new))
.collect::<Result<Vec<i64>>>()?; .collect::<Result<Vec<i64>>>()?;
let (a, b) = find_2020_2_sum(&entries)?; let (a, b) = find_2020_2_sum(&entries)?;
@ -28,8 +28,8 @@ fn part1(input: &str) -> Result<i64> {
fn part2(input: &str) -> Result<i64> { fn part2(input: &str) -> Result<i64> {
let entries = input let entries = input
.lines() .lines()
.map(|line| line.parse::<i64>().map_err(|e| err!("{}", e))) .map(|line| line.parse::<i64>().map_err(anyhow::Error::new))
.collect::<Result<Vec<i64>>>()?; .collect::<Result<Vec<_>>>()?;
let (a, b, c) = find_2020_3_sum(&entries)?; let (a, b, c) = find_2020_3_sum(&entries)?;
@ -47,7 +47,7 @@ fn find_2020_2_sum(entries: &[i64]) -> Result<(i64, i64)> {
} }
} }
Err(err!("couldn't find 2 elements that sum to 2020")) Err(anyhow!("couldn't find 2 elements that sum to 2020"))
} }
fn find_2020_3_sum(entries: &[i64]) -> Result<(i64, i64, i64)> { fn find_2020_3_sum(entries: &[i64]) -> Result<(i64, i64, i64)> {
@ -70,7 +70,7 @@ fn find_2020_3_sum(entries: &[i64]) -> Result<(i64, i64, i64)> {
} }
} }
Err(err!("couldn't find 2 elements that sum to 2020")) Err(anyhow!("couldn't find 2 elements that sum to 2020"))
} }
#[cfg(test)] #[cfg(test)]

View file

@ -1,7 +1,7 @@
use std::fmt::Write; use std::fmt::Write;
use std::str::FromStr; use std::str::FromStr;
use aoc::{err, Result}; use anyhow::{Context, Result};
const INPUT: &str = include_str!("../input/day02.txt"); const INPUT: &str = include_str!("../input/day02.txt");
@ -44,28 +44,28 @@ impl PassPolicy {
} }
impl FromStr for PassPolicy { impl FromStr for PassPolicy {
type Err = aoc::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self> { fn from_str(s: &str) -> Result<Self> {
let s = s.trim_end(); let s = s.trim_end();
let space = s let space = s
.find(' ') .find(' ')
.ok_or_else(|| err!("couldn't parse password policy: didn't find space"))?; .context("couldn't parse password policy: didn't find space")?;
let dash = s let dash = s
.find('-') .find('-')
.ok_or_else(|| err!("couldn't parse password policy: didn't find dash"))?; .context("couldn't parse password policy: didn't find dash")?;
let min_bound = s[..dash] let min_bound = s[..dash]
.parse::<usize>() .parse::<usize>()
.map_err(|e| err!("couldn't parse range: {}", e))?; .context("couldn't parse range")?;
let max_bound = s[(dash + 1)..space] let max_bound = s[(dash + 1)..space]
.parse::<usize>() .parse::<usize>()
.map_err(|e| err!("couldn't parse range: {}", e))?; .context("couldn't parse range")?;
let colon = s let colon = s
.find(':') .find(':')
.ok_or_else(|| err!("couldn't parse password policy: didn't find colon"))?; .context("couldn't parse password policy: didn't find colon")?;
let letter = s.as_bytes()[colon - 1]; let letter = s.as_bytes()[colon - 1];
@ -92,7 +92,7 @@ pub fn run() -> Result<String> {
fn part1(input: &str) -> Result<usize> { fn part1(input: &str) -> Result<usize> {
let policies = input let policies = input
.lines() .lines()
.map(|line| line.parse::<PassPolicy>().map_err(|e| err!("{}", e))) .map(|line| line.parse::<PassPolicy>())
.collect::<Result<Vec<PassPolicy>>>()?; .collect::<Result<Vec<PassPolicy>>>()?;
Ok(policies Ok(policies
@ -104,7 +104,7 @@ fn part1(input: &str) -> Result<usize> {
fn part2(input: &str) -> Result<usize> { fn part2(input: &str) -> Result<usize> {
let policies = input let policies = input
.lines() .lines()
.map(|line| line.parse::<PassPolicy>().map_err(|e| err!("{}", e))) .map(|line| line.parse::<PassPolicy>())
.collect::<Result<Vec<PassPolicy>>>()?; .collect::<Result<Vec<PassPolicy>>>()?;
Ok(policies Ok(policies

View file

@ -3,9 +3,11 @@ use std::iter::FromIterator;
use std::ops::Index; use std::ops::Index;
use std::str::FromStr; use std::str::FromStr;
use anyhow::Result;
const INPUT: &str = include_str!("../input/day03.txt"); const INPUT: &str = include_str!("../input/day03.txt");
pub fn run() -> aoc::Result<String> { pub fn run() -> Result<String> {
let mut res = String::with_capacity(128); let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 1: {}", part1(INPUT)?)?;
@ -14,13 +16,13 @@ pub fn run() -> aoc::Result<String> {
Ok(res) Ok(res)
} }
fn part1(input: &str) -> aoc::Result<usize> { fn part1(input: &str) -> Result<usize> {
let forest = input.parse()?; let forest = input.parse()?;
Ok(count_trees(&forest, (3, 1))) Ok(count_trees(&forest, (3, 1)))
} }
fn part2(input: &str) -> aoc::Result<usize> { fn part2(input: &str) -> Result<usize> {
let forest = input.parse()?; let forest = input.parse()?;
let slopes = &[(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)]; let slopes = &[(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)];
@ -89,9 +91,9 @@ impl Index<usize> for Forest {
} }
impl FromStr for Forest { impl FromStr for Forest {
type Err = aoc::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> aoc::Result<Self> { fn from_str(s: &str) -> Result<Self> {
let trees = s let trees = s
.lines() .lines()
.map(|line| line.chars().map(|c| matches!(c, '#')).collect()) .map(|line| line.chars().map(|c| matches!(c, '#')).collect())

View file

@ -3,9 +3,11 @@ use std::fmt::Write;
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use std::str::FromStr; use std::str::FromStr;
use anyhow::Result;
const INPUT: &str = include_str!("../input/day04.txt"); const INPUT: &str = include_str!("../input/day04.txt");
pub fn run() -> aoc::Result<String> { pub fn run() -> Result<String> {
let mut res = String::with_capacity(128); let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 1: {}", part1(INPUT)?)?;
@ -14,7 +16,7 @@ pub fn run() -> aoc::Result<String> {
Ok(res) Ok(res)
} }
fn get_passports(input: &str) -> aoc::Result<Vec<Passport>> { fn get_passports(input: &str) -> Result<Vec<Passport>> {
let mut passports: Vec<Passport> = Vec::new(); let mut passports: Vec<Passport> = Vec::new();
let mut passport = String::new(); let mut passport = String::new();
@ -35,13 +37,13 @@ fn get_passports(input: &str) -> aoc::Result<Vec<Passport>> {
Ok(passports) Ok(passports)
} }
fn part1(input: &str) -> aoc::Result<usize> { fn part1(input: &str) -> Result<usize> {
let passports = get_passports(input)?; let passports = get_passports(input)?;
Ok(passports.iter().filter(|p| p.is_complete()).count()) Ok(passports.iter().filter(|p| p.is_complete()).count())
} }
fn part2(input: &str) -> aoc::Result<usize> { fn part2(input: &str) -> Result<usize> {
let passports = get_passports(input)?; let passports = get_passports(input)?;
Ok(passports Ok(passports
@ -89,9 +91,9 @@ impl Passport {
} }
impl FromStr for Passport { impl FromStr for Passport {
type Err = aoc::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> aoc::Result<Self> { fn from_str(s: &str) -> Result<Self> {
let mut fields: HashMap<&str, String> = s let mut fields: HashMap<&str, String> = s
.split_whitespace() .split_whitespace()
.map(|f| { .map(|f| {

View file

@ -1,11 +1,11 @@
use std::fmt::Write; use std::fmt::Write;
use std::str::FromStr; use std::str::FromStr;
use aoc::err; use anyhow::{anyhow, bail, Context, Result};
const INPUT: &str = include_str!("../input/day05.txt"); const INPUT: &str = include_str!("../input/day05.txt");
pub fn run() -> aoc::Result<String> { pub fn run() -> Result<String> {
let mut res = String::with_capacity(128); let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 1: {}", part1(INPUT)?)?;
@ -14,26 +14,24 @@ pub fn run() -> aoc::Result<String> {
Ok(res) Ok(res)
} }
fn part1(input: &str) -> aoc::Result<usize> { fn part1(input: &str) -> Result<usize> {
let seats = input let seats = input
.lines() .lines()
.map(|line| line.parse()) .map(|line| line.parse())
.collect::<aoc::Result<Vec<Seat>>>() .collect::<Result<Vec<Seat>>>()?;
.map_err(|e| err!("{}", e))?;
seats seats
.iter() .iter()
.map(|seat| seat.id()) .map(|seat| seat.id())
.max() .max()
.ok_or_else(|| err!("0 seats processed")) .context("0 seats processed")
} }
fn part2(input: &str) -> aoc::Result<usize> { fn part2(input: &str) -> Result<usize> {
let mut seats = input let mut seats = input
.lines() .lines()
.map(|line| line.parse()) .map(|line| line.parse())
.collect::<aoc::Result<Vec<Seat>>>() .collect::<Result<Vec<Seat>>>()?;
.map_err(|e| err!("{}", e))?;
// Seats will be sorted by lexicographical order of fields thanks to `derive(PartialOrd, Ord)`, // Seats will be sorted by lexicographical order of fields thanks to `derive(PartialOrd, Ord)`,
// which should produce the same result as a manual implementation of `Ord` and `PartialOrd` // which should produce the same result as a manual implementation of `Ord` and `PartialOrd`
@ -49,7 +47,7 @@ fn part2(input: &str) -> aoc::Result<usize> {
} }
} }
Err(err!("didn't find missing seat")) Err(anyhow!("didn't find missing seat"))
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
@ -65,11 +63,11 @@ impl Seat {
} }
impl FromStr for Seat { impl FromStr for Seat {
type Err = aoc::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> aoc::Result<Self> { fn from_str(s: &str) -> Result<Self> {
if s.len() != 10 { if s.len() != 10 {
return Err(err!("Seat encoding must be 10 chars long: {}", s)); bail!("Seat encoding must be 10 chars long: {}", s);
} }
let (mut row_begin, mut row_end) = (0, 128); let (mut row_begin, mut row_end) = (0, 128);
@ -90,7 +88,7 @@ impl FromStr for Seat {
'R' => { 'R' => {
col_begin += col_range; col_begin += col_range;
} }
_ => return Err(err!("Wrong char while decoding seat: `{}`", c)), _ => bail!("Wrong char while decoding seat: `{}`", c),
} }
} }

View file

@ -1,9 +1,11 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::Write; use std::fmt::Write;
use anyhow::Result;
const INPUT: &str = include_str!("../input/day06.txt"); const INPUT: &str = include_str!("../input/day06.txt");
pub fn run() -> aoc::Result<String> { pub fn run() -> Result<String> {
let mut res = String::with_capacity(128); let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 1: {}", part1(INPUT)?)?;
@ -12,7 +14,7 @@ pub fn run() -> aoc::Result<String> {
Ok(res) Ok(res)
} }
fn get_groups(input: &str) -> aoc::Result<Vec<Group>> { fn get_groups(input: &str) -> Result<Vec<Group>> {
let mut groups = Vec::new(); let mut groups = Vec::new();
let mut answers = Vec::new(); let mut answers = Vec::new();
@ -36,7 +38,7 @@ fn get_groups(input: &str) -> aoc::Result<Vec<Group>> {
Ok(groups) Ok(groups)
} }
fn part1(input: &str) -> aoc::Result<usize> { fn part1(input: &str) -> Result<usize> {
let groups = get_groups(input)?; let groups = get_groups(input)?;
Ok(groups Ok(groups
@ -45,7 +47,7 @@ fn part1(input: &str) -> aoc::Result<usize> {
.sum()) .sum())
} }
fn part2(input: &str) -> aoc::Result<usize> { fn part2(input: &str) -> Result<usize> {
let groups = get_groups(input)?; let groups = get_groups(input)?;
Ok(groups Ok(groups

View file

@ -2,11 +2,11 @@ use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
use std::str::FromStr; use std::str::FromStr;
use aoc::err; use anyhow::{Context, Result};
const INPUT: &str = include_str!("../input/day07.txt"); const INPUT: &str = include_str!("../input/day07.txt");
pub fn run() -> aoc::Result<String> { pub fn run() -> Result<String> {
let mut res = String::with_capacity(128); let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 1: {}", part1(INPUT)?)?;
@ -15,11 +15,11 @@ pub fn run() -> aoc::Result<String> {
Ok(res) Ok(res)
} }
fn part1(input: &str) -> aoc::Result<usize> { fn part1(input: &str) -> Result<usize> {
let bag_rules = input let bag_rules = input
.lines() .lines()
.map(|line| line.parse()) .map(|line| line.parse())
.collect::<aoc::Result<Vec<BagRule>>>() .collect::<Result<Vec<BagRule>>>()
.unwrap(); .unwrap();
// create map with Key = color, Value = BagRule // create map with Key = color, Value = BagRule
@ -39,11 +39,11 @@ fn part1(input: &str) -> aoc::Result<usize> {
.count()) .count())
} }
fn part2(input: &str) -> aoc::Result<usize> { fn part2(input: &str) -> Result<usize> {
let bag_rules = input let bag_rules = input
.lines() .lines()
.map(|line| line.parse()) .map(|line| line.parse())
.collect::<aoc::Result<Vec<BagRule>>>() .collect::<Result<Vec<BagRule>>>()
.unwrap(); .unwrap();
// create map with Key = color, Value = BagRule // create map with Key = color, Value = BagRule
@ -102,9 +102,9 @@ impl BagRule {
} }
impl FromStr for BagRule { impl FromStr for BagRule {
type Err = aoc::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> aoc::Result<Self> { fn from_str(s: &str) -> Result<Self> {
let words: Vec<&str> = s.split(' ').collect(); let words: Vec<&str> = s.split(' ').collect();
// let's assume our input is always valid for now // let's assume our input is always valid for now
@ -130,7 +130,7 @@ impl FromStr for BagRule {
number => { number => {
let n = number let n = number
.parse() .parse()
.map_err(|e| err!("couldn't parse number `{}` in bag rule", e))?; .context("couldn't parse number in bag rule")?;
let adjective = words[1]; let adjective = words[1];
let color = words[2]; let color = words[2];
@ -169,7 +169,7 @@ mod tests {
let bag_rules = PROVIDED1 let bag_rules = PROVIDED1
.lines() .lines()
.map(|line| line.parse()) .map(|line| line.parse())
.collect::<aoc::Result<Vec<BagRule>>>() .collect::<Result<Vec<BagRule>>>()
.unwrap(); .unwrap();
let expected = vec![ let expected = vec![

View file

@ -1,11 +1,11 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::Write; use std::fmt::Write;
use aoc::err; use anyhow::{anyhow, bail, Context, Result};
const INPUT: &str = include_str!("../input/day08.txt"); const INPUT: &str = include_str!("../input/day08.txt");
pub fn run() -> aoc::Result<String> { pub fn run() -> Result<String> {
let mut res = String::with_capacity(128); let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 1: {}", part1(INPUT)?)?;
@ -14,25 +14,25 @@ pub fn run() -> aoc::Result<String> {
Ok(res) Ok(res)
} }
fn part1(input: &str) -> aoc::Result<i64> { fn part1(input: &str) -> Result<i64> {
let instructions = input let instructions = input
.lines() .lines()
.map(|line| line.parse()) .map(|line| line.parse())
.collect::<aoc::Result<Vec<Instruction>>>()?; .collect::<Result<Vec<Instruction>>>()?;
let mut interpreter = Interpreter::new(instructions); let mut interpreter = Interpreter::new(instructions);
Ok(match interpreter.run() { Ok(match interpreter.run() {
ExitStatus::InfiniteLoop(value) => value, ExitStatus::InfiniteLoop(value) => value,
ExitStatus::End(_) => return Err(err!("interpreter doesn't have an infinite loop")), ExitStatus::End(_) => bail!("interpreter doesn't have an infinite loop"),
}) })
} }
fn part2(input: &str) -> aoc::Result<i64> { fn part2(input: &str) -> Result<i64> {
let instructions = input let instructions = input
.lines() .lines()
.map(|line| line.parse()) .map(|line| line.parse())
.collect::<aoc::Result<Vec<Instruction>>>()?; .collect::<Result<Vec<Instruction>>>()?;
for idx in 0..instructions.len() { for idx in 0..instructions.len() {
let mut instructions = instructions.clone(); let mut instructions = instructions.clone();
@ -51,7 +51,7 @@ fn part2(input: &str) -> aoc::Result<i64> {
} }
} }
Err(err!( Err(anyhow!(
"interpreter always had an infinite loop, no solution found" "interpreter always had an infinite loop, no solution found"
)) ))
} }
@ -110,21 +110,21 @@ enum Instruction {
} }
impl std::str::FromStr for Instruction { impl std::str::FromStr for Instruction {
type Err = aoc::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> aoc::Result<Self> { fn from_str(s: &str) -> Result<Self> {
let space = s.find(' ').ok_or_else(|| err!("couldn't split on space"))?; let space = s.find(' ').context("couldn't split on space")?;
let inst = &s[..space]; let inst = &s[..space];
let arg = s[(space + 1)..] let arg = s[(space + 1)..]
.parse() .parse()
.map_err(|e| err!("couldn't parse argument for instruction: {}", e))?; .context("couldn't parse argument for instruction")?;
Ok(match inst { Ok(match inst {
"acc" => Self::Acc(arg), "acc" => Self::Acc(arg),
"jmp" => Self::Jmp(arg), "jmp" => Self::Jmp(arg),
"nop" => Self::Nop(arg), "nop" => Self::Nop(arg),
_ => return Err(err!("unrecognized instruction `{}`", inst)), _ => bail!("unrecognized instruction `{}`", inst),
}) })
} }
} }

View file

@ -1,10 +1,10 @@
use std::fmt::Write; use std::fmt::Write;
use aoc::err; use anyhow::{anyhow, Result};
const INPUT: &str = include_str!("../input/day09.txt"); const INPUT: &str = include_str!("../input/day09.txt");
pub fn run() -> aoc::Result<String> { pub fn run() -> Result<String> {
let mut res = String::with_capacity(128); let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 1: {}", part1(INPUT)?)?;
@ -27,7 +27,7 @@ fn find_pair_sum(data: &[u64], total: u64) -> Option<(u64, u64)> {
None None
} }
fn find_outlier(numbers: &[u64], preamble_size: usize) -> aoc::Result<(u64, usize)> { fn find_outlier(numbers: &[u64], preamble_size: usize) -> Result<(u64, usize)> {
// start checking numbers after the preamble only // start checking numbers after the preamble only
for i in preamble_size..numbers.len() { for i in preamble_size..numbers.len() {
let preamble = &numbers[(i - preamble_size)..i]; let preamble = &numbers[(i - preamble_size)..i];
@ -39,22 +39,21 @@ fn find_outlier(numbers: &[u64], preamble_size: usize) -> aoc::Result<(u64, usiz
} }
} }
Err(err!("couldn't find number with that property")) Err(anyhow!("couldn't find number with that property"))
} }
fn part1(input: &str) -> aoc::Result<u64> { fn part1(input: &str) -> Result<u64> {
let numbers = input let numbers = input
.lines() .lines()
.map(|line| line.parse::<u64>()) .map(|line| line.parse::<u64>().map_err(anyhow::Error::new))
.collect::<Result<Vec<u64>, _>>() .collect::<Result<Vec<u64>, _>>()?;
.map_err(|e| err!("couldn't parse number: {}", e))?;
let (solution, _) = find_outlier(&numbers, 25)?; let (solution, _) = find_outlier(&numbers, 25)?;
Ok(solution) Ok(solution)
} }
fn find_contiguous_range(numbers: &[u64], total: u64) -> aoc::Result<(u64, u64)> { fn find_contiguous_range(numbers: &[u64], total: u64) -> Result<(u64, u64)> {
// compute cumulated sums for the whole range // compute cumulated sums for the whole range
let (sums, _) = numbers.iter().fold((vec![0], 0), |(mut vec, acc), n| { let (sums, _) = numbers.iter().fold((vec![0], 0), |(mut vec, acc), n| {
let acc = acc + n; let acc = acc + n;
@ -74,15 +73,14 @@ fn find_contiguous_range(numbers: &[u64], total: u64) -> aoc::Result<(u64, u64)>
} }
} }
Err(err!("couldn't find number with that property")) Err(anyhow!("couldn't find number with that property"))
} }
fn part2(input: &str) -> aoc::Result<u64> { fn part2(input: &str) -> Result<u64> {
let numbers = input let numbers = input
.lines() .lines()
.map(|line| line.parse::<u64>()) .map(|line| line.parse::<u64>().map_err(anyhow::Error::new))
.collect::<Result<Vec<u64>, _>>() .collect::<Result<Vec<u64>, _>>()?;
.map_err(|e| err!("couldn't parse number: {}", e))?;
let (outlier, idx) = find_outlier(&numbers, 25)?; let (outlier, idx) = find_outlier(&numbers, 25)?;

View file

@ -1,11 +1,11 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
use aoc::err; use anyhow::{bail, Result};
const INPUT: &str = include_str!("../input/day10.txt"); const INPUT: &str = include_str!("../input/day10.txt");
pub fn run() -> aoc::Result<String> { pub fn run() -> Result<String> {
let mut res = String::with_capacity(128); let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 1: {}", part1(INPUT)?)?;
@ -14,17 +14,14 @@ pub fn run() -> aoc::Result<String> {
Ok(res) Ok(res)
} }
fn part1(input: &str) -> aoc::Result<usize> { fn part1(input: &str) -> Result<usize> {
let mut jolts = input let mut jolts = input
.lines() .lines()
.map(|line| { .map(|line| line.parse().map_err(anyhow::Error::new))
line.parse() .collect::<Result<Vec<u64>>>()?;
.map_err(|e| err!("couldn't parse joltage: {}", e))
})
.collect::<aoc::Result<Vec<u64>>>()?;
if jolts.is_empty() { if jolts.is_empty() {
return Err(err!("input was empty!")); bail!("input was empty!");
} }
// charging outlet can be added // charging outlet can be added
@ -80,17 +77,14 @@ fn find_possibilities(jolts: &[u64], possibilities: &mut HashMap<u64, usize>) ->
possibilities_from_here possibilities_from_here
} }
fn part2(input: &str) -> aoc::Result<usize> { fn part2(input: &str) -> Result<usize> {
let mut jolts = input let mut jolts = input
.lines() .lines()
.map(|line| { .map(|line| line.parse().map_err(anyhow::Error::new))
line.parse() .collect::<Result<Vec<u64>>>()?;
.map_err(|e| err!("couldn't parse joltage: {}", e))
})
.collect::<aoc::Result<Vec<u64>>>()?;
if jolts.is_empty() { if jolts.is_empty() {
return Err(err!("input was empty!")); bail!("input was empty!");
} }
// charging outlet can be added // charging outlet can be added

View file

@ -1,10 +1,10 @@
use std::fmt::Write; use std::fmt::Write;
use aoc::err; use anyhow::{anyhow, Result};
const INPUT: &str = include_str!("../input/day11.txt"); const INPUT: &str = include_str!("../input/day11.txt");
pub fn run() -> aoc::Result<String> { pub fn run() -> Result<String> {
let mut res = String::with_capacity(128); let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 1: {}", part1(INPUT)?)?;
@ -13,7 +13,7 @@ pub fn run() -> aoc::Result<String> {
Ok(res) Ok(res)
} }
fn part1(input: &str) -> aoc::Result<usize> { fn part1(input: &str) -> Result<usize> {
let mut layout: Layout = input.parse()?; let mut layout: Layout = input.parse()?;
let occupied_threshold = 4; let occupied_threshold = 4;
@ -22,7 +22,7 @@ fn part1(input: &str) -> aoc::Result<usize> {
Ok(layout.occupied_seats()) Ok(layout.occupied_seats())
} }
fn part2(input: &str) -> aoc::Result<usize> { fn part2(input: &str) -> Result<usize> {
let mut layout: Layout = input.parse()?; let mut layout: Layout = input.parse()?;
let occupied_threshold = 5; let occupied_threshold = 5;
@ -177,9 +177,9 @@ impl std::ops::IndexMut<usize> for Layout {
} }
impl std::str::FromStr for Layout { impl std::str::FromStr for Layout {
type Err = aoc::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> aoc::Result<Self> { fn from_str(s: &str) -> Result<Self> {
let grid = s let grid = s
.lines() .lines()
.map(|line| { .map(|line| {
@ -188,11 +188,11 @@ impl std::str::FromStr for Layout {
'.' => Ok(Cell::Floor), '.' => Ok(Cell::Floor),
'L' => Ok(Cell::EmptySeat), 'L' => Ok(Cell::EmptySeat),
'#' => Ok(Cell::OccupiedSeat), '#' => Ok(Cell::OccupiedSeat),
_ => Err(err!("unknown char `{}`", c)), _ => Err(anyhow!("unknown char `{}`", c)),
}) })
.collect() .collect()
}) })
.collect::<aoc::Result<Grid>>()?; .collect::<Result<Grid>>()?;
let height = grid.len(); let height = grid.len();
let width = grid[0].len(); let width = grid[0].len();

View file

@ -1,10 +1,10 @@
use std::fmt::Write; use std::fmt::Write;
use aoc::err; use anyhow::{bail, Context, Result};
const INPUT: &str = include_str!("../input/day12.txt"); const INPUT: &str = include_str!("../input/day12.txt");
pub fn run() -> aoc::Result<String> { pub fn run() -> Result<String> {
let mut res = String::with_capacity(128); let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 1: {}", part1(INPUT)?)?;
@ -13,11 +13,11 @@ pub fn run() -> aoc::Result<String> {
Ok(res) Ok(res)
} }
fn part1(input: &str) -> aoc::Result<i64> { fn part1(input: &str) -> Result<i64> {
let actions: Vec<Action> = input let actions: Vec<Action> = input
.lines() .lines()
.map(|line| line.parse()) .map(|line| line.parse())
.collect::<aoc::Result<_>>()?; .collect::<Result<_>>()?;
let mut ship = Ship::new(); let mut ship = Ship::new();
@ -28,11 +28,11 @@ fn part1(input: &str) -> aoc::Result<i64> {
Ok(ship.manhattan_distance()) Ok(ship.manhattan_distance())
} }
fn part2(input: &str) -> aoc::Result<i64> { fn part2(input: &str) -> Result<i64> {
let actions: Vec<Action> = input let actions: Vec<Action> = input
.lines() .lines()
.map(|line| line.parse()) .map(|line| line.parse())
.collect::<aoc::Result<_>>()?; .collect::<Result<_>>()?;
let mut ship = Ship::new(); let mut ship = Ship::new();
@ -118,9 +118,9 @@ struct Action {
} }
impl std::str::FromStr for Action { impl std::str::FromStr for Action {
type Err = aoc::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> aoc::Result<Self> { fn from_str(s: &str) -> Result<Self> {
debug_assert!( debug_assert!(
s.len() >= 2, s.len() >= 2,
"tried to parse action but it is too short: `{}`", "tried to parse action but it is too short: `{}`",
@ -129,7 +129,7 @@ impl std::str::FromStr for Action {
let letter = s let letter = s
.chars() .chars()
.next() .next()
.ok_or_else(|| err!("couldn't parse action: empty string"))?; .context("couldn't parse action: empty string")?;
let kind = match letter { let kind = match letter {
'N' => ActionKind::Move(Direction::North), 'N' => ActionKind::Move(Direction::North),
@ -142,12 +142,10 @@ impl std::str::FromStr for Action {
'F' => ActionKind::Forward, 'F' => ActionKind::Forward,
_ => return Err(err!("couldn't parse action with letter `{}`", letter)), _ => bail!("couldn't parse action with letter `{}`", letter),
}; };
let arg = s[1..] let arg = s[1..].parse().context("couldn't parse action arg")?;
.parse()
.map_err(|e| err!("couldn't parse action arg: {}", e))?;
Ok(Self { kind, arg }) Ok(Self { kind, arg })
} }

View file

@ -1,10 +1,10 @@
use std::fmt::Write; use std::fmt::Write;
use aoc::err; use anyhow::{Context, Result};
const INPUT: &str = include_str!("../input/day13.txt"); const INPUT: &str = include_str!("../input/day13.txt");
pub fn run() -> aoc::Result<String> { pub fn run() -> Result<String> {
let mut res = String::with_capacity(128); let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 1: {}", part1(INPUT)?)?;
@ -13,27 +13,27 @@ pub fn run() -> aoc::Result<String> {
Ok(res) Ok(res)
} }
fn part1(input: &str) -> aoc::Result<u64> { fn part1(input: &str) -> Result<u64> {
let mut lines = input.lines(); let mut lines = input.lines();
let earliest_timestamp = lines let earliest_timestamp = lines
.next() .next()
.ok_or_else(|| err!("input was empty"))? .context("input was empty")?
.parse::<u64>() .parse::<u64>()
.map_err(|e| err!("couldn't parse first line: {}", e))?; .context("couldn't parse first line")?;
let bus_ids = lines let bus_ids = lines
.next() .next()
.ok_or_else(|| err!("no second line"))? .context("no second line")?
.split(',') .split(',')
.filter_map(|num| { .filter_map(|num| {
if num == "x" { if num == "x" {
None None
} else { } else {
Some(num.parse::<u64>().map_err(|e| err!("{}", e))) Some(num.parse::<u64>().map_err(anyhow::Error::new))
} }
}) })
.collect::<aoc::Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
let (bus_id, earliest_departure) = bus_ids let (bus_id, earliest_departure) = bus_ids
.iter() .iter()
@ -47,16 +47,16 @@ fn part1(input: &str) -> aoc::Result<u64> {
Ok(bus_id * (earliest_departure - earliest_timestamp)) Ok(bus_id * (earliest_departure - earliest_timestamp))
} }
fn part2(input: &str) -> aoc::Result<u64> { fn part2(input: &str) -> Result<u64> {
let mut lines = input.lines(); let mut lines = input.lines();
// we don't need the first line anymore, skip it // we don't need the first line anymore, skip it
lines.next().ok_or_else(|| err!("input was empty"))?; lines.next().context("input was empty")?;
find_timestamp(lines.next().ok_or_else(|| err!("no second line"))?) find_timestamp(lines.next().context("no second line")?)
} }
fn find_timestamp(input: &str) -> aoc::Result<u64> { fn find_timestamp(input: &str) -> Result<u64> {
let bus_ids: Vec<(u64, u64)> = input let bus_ids: Vec<(u64, u64)> = input
.split(',') .split(',')
.enumerate() .enumerate()
@ -64,14 +64,14 @@ fn find_timestamp(input: &str) -> aoc::Result<u64> {
if num == "x" { if num == "x" {
None None
} else { } else {
Some((idx as u64, num.parse::<u64>().map_err(|e| err!("{}", e)))) Some((idx as u64, num.parse::<u64>().map_err(anyhow::Error::new)))
} }
}) })
.map(|(idx, res)| match res { .map(|(idx, res)| match res {
Ok(num) => Ok((idx, num)), Ok(num) => Ok((idx, num)),
Err(e) => Err(e), Err(e) => Err(e),
}) })
.collect::<aoc::Result<_>>()?; .collect::<Result<_>>()?;
// previous constraints is empty for now // previous constraints is empty for now
let mut current_solution = 0; let mut current_solution = 0;

View file

@ -1,11 +1,11 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
use aoc::err; use anyhow::{anyhow, bail, Context, Result};
const INPUT: &str = include_str!("../input/day14.txt"); const INPUT: &str = include_str!("../input/day14.txt");
pub fn run() -> aoc::Result<String> { pub fn run() -> Result<String> {
let mut res = String::with_capacity(128); let mut res = String::with_capacity(128);
writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 1: {}", part1(INPUT)?)?;
@ -14,7 +14,7 @@ pub fn run() -> aoc::Result<String> {
Ok(res) Ok(res)
} }
fn part1(input: &str) -> aoc::Result<u64> { fn part1(input: &str) -> Result<u64> {
let mut program: Program = input.parse()?; let mut program: Program = input.parse()?;
program.run_part1()?; program.run_part1()?;
@ -22,7 +22,7 @@ fn part1(input: &str) -> aoc::Result<u64> {
Ok(program.memory_sum()) Ok(program.memory_sum())
} }
fn part2(input: &str) -> aoc::Result<u64> { fn part2(input: &str) -> Result<u64> {
let mut program: Program = input.parse()?; let mut program: Program = input.parse()?;
program.run_part2()?; program.run_part2()?;
@ -175,9 +175,9 @@ impl BitMask {
} }
impl std::str::FromStr for BitMask { impl std::str::FromStr for BitMask {
type Err = aoc::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> aoc::Result<Self> { fn from_str(s: &str) -> Result<Self> {
let masks = s let masks = s
.chars() .chars()
.rev() .rev()
@ -187,10 +187,10 @@ impl std::str::FromStr for BitMask {
'1' => Ok(Mask::One), '1' => Ok(Mask::One),
'0' => Ok(Mask::Zero), '0' => Ok(Mask::Zero),
'X' => Ok(Mask::Floating), 'X' => Ok(Mask::Floating),
_ => Err(err!("unknown character in mask: `{}`", c)), _ => Err(anyhow!("unknown character in mask: `{}`", c)),
} }
}) })
.collect::<aoc::Result<_>>()?; .collect::<Result<_>>()?;
Ok(BitMask { masks }) Ok(BitMask { masks })
} }
@ -203,23 +203,17 @@ enum Instruction {
} }
impl std::str::FromStr for Instruction { impl std::str::FromStr for Instruction {
type Err = aoc::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> aoc::Result<Self> { fn from_str(s: &str) -> Result<Self> {
let mut words = s.split(' '); let mut words = s.split(' ');
let first = words let first = words.next().context("missing first word in instruction")?;
.next() let second = words.next().context("missing second word in instruction")?;
.ok_or_else(|| err!("missing first word in instruction"))?; let third = words.next().context("missing third word in instruction")?;
let second = words
.next()
.ok_or_else(|| err!("missing second word in instruction"))?;
let third = words
.next()
.ok_or_else(|| err!("missing third word in instruction"))?;
if second != "=" { if second != "=" {
return Err(err!("expected `=` as second word in instruction: `{}`", s)); bail!("expected `=` as second word in instruction: `{}`", s);
} }
if first == "mask" { if first == "mask" {
@ -227,18 +221,16 @@ impl std::str::FromStr for Instruction {
} else { } else {
let left_bracket = first let left_bracket = first
.find('[') .find('[')
.ok_or_else(|| err!("couldn't find bracket in memory instruction"))?; .context("couldn't find bracket in memory instruction")?;
let right_bracket = first let right_bracket = first
.find(']') .find(']')
.ok_or_else(|| err!("couldn't find bracket in memory instruction"))?; .context("couldn't find bracket in memory instruction")?;
let offset = first[(left_bracket + 1)..right_bracket] let offset = first[(left_bracket + 1)..right_bracket]
.parse() .parse()
.map_err(|e| err!("couldn't parse memory offset: `{}`", e))?; .context("couldn't parse memory offset")?;
let value = third let value = third.parse().context("couldn't parse memory offset")?;
.parse()
.map_err(|e| err!("couldn't parse memory offset: `{}`", e))?;
Ok(Self::MemWrite { offset, value }) Ok(Self::MemWrite { offset, value })
} }
@ -252,7 +244,7 @@ struct Program {
} }
impl Program { impl Program {
fn run_part1(&mut self) -> aoc::Result<()> { fn run_part1(&mut self) -> Result<()> {
for inst in &self.instructions { for inst in &self.instructions {
match inst { match inst {
Instruction::ChangeMask(bitmask) => self.current_mask = Some(bitmask.clone()), Instruction::ChangeMask(bitmask) => self.current_mask = Some(bitmask.clone()),
@ -263,7 +255,7 @@ impl Program {
.insert(*offset, bitmask.apply_no_floating(*value)); .insert(*offset, bitmask.apply_no_floating(*value));
} }
None => { None => {
return Err(err!("tried to execute MemWrite but mask isn't initialized")) bail!("tried to execute MemWrite but mask isn't initialized")
} }
}, },
} }
@ -272,7 +264,7 @@ impl Program {
Ok(()) Ok(())
} }
fn run_part2(&mut self) -> aoc::Result<()> { fn run_part2(&mut self) -> Result<()> {
for inst in &self.instructions { for inst in &self.instructions {
match inst { match inst {
Instruction::ChangeMask(bitmask) => self.current_mask = Some(bitmask.clone()), Instruction::ChangeMask(bitmask) => self.current_mask = Some(bitmask.clone()),
@ -284,7 +276,7 @@ impl Program {
} }
} }
None => { None => {
return Err(err!("tried to execute MemWrite but mask isn't initialized")) bail!("tried to execute MemWrite but mask isn't initialized")
} }
}, },
} }
@ -299,13 +291,10 @@ impl Program {
} }
impl std::str::FromStr for Program { impl std::str::FromStr for Program {
type Err = aoc::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> aoc::Result<Self> { fn from_str(s: &str) -> Result<Self> {
let instructions = s let instructions = s.lines().map(|line| line.parse()).collect::<Result<_>>()?;
.lines()
.map(|line| line.parse())
.collect::<aoc::Result<_>>()?;
Ok(Program { Ok(Program {
instructions, instructions,

View file

@ -1,5 +1,6 @@
use anyhow::Result;
use aoc::DayFunc; use aoc::DayFunc;
use aoc::Result;
use aoc2020::day01; use aoc2020::day01;
use aoc2020::day02; use aoc2020::day02;